1 /*
2 * player.c - player module
3 * Copyright (C) 2008-2010 Alexandre Martins <alemartf(at)gmail(dot)com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <math.h>
21 #include "actor.h"
22 #include "player.h"
23 #include "brick.h"
24 #include "enemy.h"
25 #include "item.h"
26 #include "items/ring.h"
27 #include "items/falglasses.h"
28 #include "../core/global.h"
29 #include "../core/audio.h"
30 #include "../core/util.h"
31 #include "../core/stringutil.h"
32 #include "../core/timer.h"
33 #include "../core/logfile.h"
34 #include "../core/input.h"
35 #include "../core/sprite.h"
36 #include "../core/soundfactory.h"
37 #include "../scenes/level.h"
38
39
40 /* Uncomment to show the collision detectors */
41 /* #define SHOW_COLLISION_DETECTORS */
42
43
44 /* private data */
45 #define NATURAL_ANGLE 0
46 #define LOCKACCEL_NONE 0
47 #define LOCKACCEL_LEFT 1
48 #define LOCKACCEL_RIGHT 2
49 static int rings, hundred_rings;
50 static int lives;
51 static int score;
52 static const char *get_sprite_id(int player_type);
53 static void update_shield(player_t *p);
54 static void update_glasses(player_t *p);
55 static void drop_glasses(player_t *p);
56 static int inside_loop(player_t *p);
57 static int got_crushed(player_t *p, brick_t *brick_up, brick_t *brick_right, brick_t *brick_down, brick_t *brick_left);
58 static void stickyphysics_hack(player_t *player, brick_list_t *brick_list, brick_t **brick_downleft, brick_t **brick_down, brick_t **brick_downright);
59
60
61 /*
62 * player_create()
63 * Creates a player
64 */
player_create(int type)65 player_t *player_create(int type)
66 {
67 int i;
68 player_t *p = mallocx(sizeof *p);
69
70 logfile_message("player_create(%d)", type);
71
72 switch(type) {
73 case PL_SONIC: p->name = str_dup("Surge"); break;
74 case PL_TAILS: p->name = str_dup("Neon"); break;
75 case PL_KNUCKLES: p->name = str_dup("Charge"); break;
76 default: p->name = str_dup("Unknown"); break;
77 }
78
79 p->type = type;
80 p->actor = actor_create();
81 p->disable_movement = FALSE;
82 p->in_locked_area = FALSE;
83 p->at_some_border = FALSE;
84
85 p->spin = p->spin_dash = p->braking = p->flying = p->climbing = p->landing = p->spring = FALSE;
86 p->is_fire_jumping = FALSE;
87 p->getting_hit = p->dying = p->dead = p->blinking = FALSE;
88 p->on_moveable_platform = FALSE;
89 p->lock_accel = LOCKACCEL_NONE;
90 p->flight_timer = p->blink_timer = p->death_timer = 0.0;
91 p->disable_jump_for = 0.0;
92
93 p->glasses = actor_create();
94 p->got_glasses = FALSE;
95
96 p->shield = actor_create();
97 p->shield_type = SH_NONE;
98
99 p->invincible = FALSE;
100 p->invtimer = 0;
101 for(i=0; i<PLAYER_MAX_INVSTAR; i++) {
102 p->invstar[i] = actor_create();
103 actor_change_animation(p->invstar[i], sprite_get_animation("SD_INVSTAR", 0));
104 }
105
106 p->got_speedshoes = FALSE;
107 p->speedshoes_timer = 0;
108
109 p->disable_wall = PLAYER_WALL_NONE;
110 p->entering_loop = FALSE;
111 p->at_loopfloortop = FALSE;
112 p->bring_to_back = FALSE;
113
114 switch(p->type) {
115 case PL_SONIC:
116 p->actor->acceleration = 250;
117 p->actor->maxspeed = 700;
118 p->actor->jump_strength = 400;
119 p->actor->input = input_create_user();
120 actor_change_animation( p->actor , sprite_get_animation(get_sprite_id(PL_SONIC), 0) );
121 break;
122
123 case PL_TAILS:
124 p->actor->acceleration = 200;
125 p->actor->maxspeed = 600;
126 p->actor->jump_strength = 360;
127 p->actor->input = input_create_user();
128 actor_change_animation( p->actor , sprite_get_animation(get_sprite_id(PL_TAILS), 0) );
129 break;
130
131 case PL_KNUCKLES:
132 p->actor->acceleration = 200;
133 p->actor->maxspeed = 600;
134 p->actor->jump_strength = 360;
135 p->actor->input = input_create_user();
136 actor_change_animation( p->actor , sprite_get_animation(get_sprite_id(PL_KNUCKLES), 0) );
137 break;
138 }
139
140 hundred_rings = rings = 0;
141 logfile_message("player_create() ok");
142 return p;
143 }
144
145
146 /*
147 * player_destroy()
148 * Destroys a player
149 */
player_destroy(player_t * player)150 void player_destroy(player_t *player)
151 {
152 int i;
153
154 for(i=0; i<PLAYER_MAX_INVSTAR; i++)
155 actor_destroy(player->invstar[i]);
156
157 actor_destroy(player->glasses);
158 actor_destroy(player->actor);
159 free(player->name);
160 free(player);
161 }
162
163
164
165 /*
166 * player_update()
167 * Updates the player
168 */
player_update(player_t * player,player_t * team[3],brick_list_t * brick_list)169 void player_update(player_t *player, player_t *team[3], brick_list_t *brick_list)
170 {
171 actor_t *act = player->actor;
172
173 if(player->blinking) {
174 player->blink_timer += timer_get_delta();
175 act->visible = (timer_get_ticks() % 250) < 125;
176 if(player->blink_timer >= PLAYER_MAX_BLINK) {
177 player->getting_hit = player->blinking = FALSE;
178 act->visible = TRUE;
179 }
180 }
181
182 if(player->disable_movement) {
183 if(player->spin)
184 actor_change_animation(player->actor, sprite_get_animation(get_sprite_id(player->type), 3));
185 else if(player->spring)
186 actor_change_animation(player->actor, sprite_get_animation(get_sprite_id(player->type), 13));
187 }
188 else
189 actor_move(act, player_platform_movement(player, team, brick_list, level_gravity()));
190 }
191
192
193 /*
194 * player_render()
195 * Rendering function
196 */
player_render(player_t * player,v2d_t camera_position)197 void player_render(player_t *player, v2d_t camera_position)
198 {
199 actor_t *act = player->actor;
200 v2d_t hot_spot = act->hot_spot;
201 v2d_t position = act->position;
202 v2d_t s_hot_spot = v2d_new(0,0);
203 v2d_t starpos;
204 int i, invangle[PLAYER_MAX_INVSTAR];
205 float x, angoff, ang = act->angle, s_ang = 0;
206
207
208
209 /* invencibility stars */
210 if(player->invincible) {
211 int maxf = sprite_get_animation("SD_INVSTAR", 0)->frame_count;
212 player->invtimer += timer_get_delta();
213
214 for(i=0; i<PLAYER_MAX_INVSTAR; i++) {
215 invangle[i] = (180*4) * timer_get_ticks()*0.001 + (i+1)*(360/PLAYER_MAX_INVSTAR);
216 starpos.x = 30*cos(invangle[i]*PI/180);
217 starpos.y = ((timer_get_ticks()+i*400)%2000)/40;
218 starpos = v2d_rotate(starpos,ang);
219 player->invstar[i]->position.x = act->position.x + starpos.x;
220 player->invstar[i]->position.y = act->position.y - starpos.y + 5;
221 actor_change_animation_frame(player->invstar[i], random(maxf));
222 }
223
224 if(player->invtimer >= PLAYER_MAX_INVINCIBILITY)
225 player->invincible = FALSE;
226 }
227
228
229 /* shields and glasses */
230 if(player->got_glasses)
231 update_glasses(player);
232
233 if(player->shield_type != SH_NONE)
234 update_shield(player);
235
236
237
238 /* player's specific routines (before rendering) */
239 switch(player->type) {
240 case PL_SONIC:
241 break;
242
243 case PL_TAILS:
244 /* tails' jump hack */
245 if(act->is_jumping && act->animation == sprite_get_animation(get_sprite_id(PL_TAILS), 3)) {
246 int rotate = ((fabs(act->speed.x)>100) || input_button_down(act->input,IB_RIGHT) || input_button_down(act->input,IB_LEFT));
247 int left = (act->mirror & IF_HFLIP);
248 act->hot_spot = v2d_new(actor_image(act)->w*0.5, actor_image(act)->h*0.9);
249 if(act->speed.y > 0 && !rotate) act->hot_spot.x *= 0.9/0.5;
250 if(act->speed.y < 0) {
251 angoff = left ? 3*PI/2 : PI/2;
252 act->angle = ang + angoff;
253 if(rotate)
254 act->angle -= (left?-1:1) * (PI/2) * (act->jump_strength+act->speed.y)/act->jump_strength;
255 else
256 act->position.x -= actor_image(act)->h*(left?0.5:0.0);
257 }
258 else {
259 angoff = left ? PI/2 : 3*PI/2;
260 act->angle = ang + angoff;
261 if(rotate) {
262 if(act->speed.y < act->jump_strength)
263 act->angle += (left?-1:1) * (PI/2) * (act->jump_strength-act->speed.y)/act->jump_strength;
264 }
265 else
266 act->position.x += actor_image(act)->h*(left?0.1:-0.2);
267 }
268
269 /* fix shield position */
270 if(player->shield_type != SH_NONE) {
271 v2d_t voff;
272 if(rotate)
273 voff = v2d_rotate(v2d_new(left?-13:13,-13), -act->angle);
274 else if(act->mirror & IF_HFLIP)
275 voff = v2d_new((act->speed.y>0) ? -13 : 13, -15);
276 else
277 voff = v2d_new((act->speed.y>0 ? 7 : -7), -15);
278 s_ang = player->shield->angle;
279 s_hot_spot = player->shield->hot_spot;
280 player->shield->position = v2d_add(act->position, voff);
281 }
282 }
283 break;
284
285 case PL_KNUCKLES:
286 break;
287 }
288
289
290 /* rendering */
291 for(i=0;i<PLAYER_MAX_INVSTAR && player->invincible;i++) {
292 if(invangle[i]%360 >= 180)
293 actor_render(player->invstar[i], camera_position);
294 }
295
296 x = act->angle;
297 act->angle = (act->is_jumping || player->spin) ? x : old_school_angle(x);
298 actor_render(act, camera_position);
299 act->angle = x;
300
301 if(player->got_glasses)
302 actor_render(player->glasses, camera_position);
303 if(player->shield_type != SH_NONE)
304 actor_render(player->shield, camera_position);
305 for(i=0;i<PLAYER_MAX_INVSTAR && player->invincible;i++) {
306 if(invangle[i]%360 < 180)
307 actor_render(player->invstar[i], camera_position);
308 }
309
310
311 /* player's specific routines (after rendering) */
312 switch(player->type) {
313 case PL_SONIC:
314 break;
315
316 case PL_TAILS:
317 if(act->is_jumping && act->animation == sprite_get_animation(get_sprite_id(PL_TAILS), 3)) {
318 act->position = position;
319 act->angle = ang;
320 act->hot_spot = hot_spot;
321
322 if(player->shield_type != SH_NONE) {
323 player->shield->angle = s_ang;
324 player->shield->hot_spot = s_hot_spot;
325 }
326 }
327 break;
328
329 case PL_KNUCKLES:
330 break;
331 }
332
333
334 /* show collision detectors (debug) */
335 #ifdef SHOW_COLLISION_DETECTORS
336 if(player->type == PL_TAILS) {
337 float diff = -2, sqrsize = 2, top=0, middle=0, lateral=0;
338 int slope = !((fabs(act->angle)<EPSILON)||(fabs(act->angle-PI/2)<EPSILON)||(fabs(act->angle-PI)<EPSILON)||(fabs(act->angle-3*PI/2)<EPSILON));
339 switch(player->type) {
340 case PL_SONIC:
341 if(!slope) { top = 0.7; middle = 0.5; lateral = 0.4; }
342 else { top = 1.0; middle = 0.8; lateral = 0.5; }
343 break;
344
345 case PL_TAILS:
346 if(!slope) { top = 0.7; middle = 0.5; lateral = 0.25; }
347 else { top = 1.0; middle = 0.7; lateral = 0.25; }
348 break;
349
350 case PL_KNUCKLES:
351 if(!slope) { top = 0.7; middle = 0.5; lateral = 0.25; }
352 else { top = 1.0; middle = 0.7; lateral = 0.25; }
353 break;
354 }
355 {
356 float frame_width=actor_image(act)->w, frame_height=actor_image(act)->h;
357 float offx=camera_position.x-VIDEO_SCREEN_W/2;
358 float offy=camera_position.y-VIDEO_SCREEN_H/2;
359 v2d_t feet = act->position;
360 v2d_t up = v2d_add ( feet , v2d_rotate( v2d_new(0, -frame_height*top+diff), -act->angle) );
361 v2d_t down = v2d_add ( feet , v2d_rotate( v2d_new(0, -diff), -act->angle) );
362 v2d_t left = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width*lateral+diff, -frame_height*middle), -act->angle) );
363 v2d_t right = v2d_add ( feet , v2d_rotate( v2d_new(frame_width*lateral-diff, -frame_height*middle), -act->angle) );
364 v2d_t upleft = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width*lateral+diff, -frame_height*top+diff), -act->angle) );
365 v2d_t upright = v2d_add ( feet , v2d_rotate( v2d_new(frame_width*lateral-diff, -frame_height*top+diff), -act->angle) );
366 v2d_t downleft = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width*lateral+diff, -diff), -act->angle) );
367 v2d_t downright = v2d_add ( feet , v2d_rotate( v2d_new(frame_width*lateral-diff, -diff), -act->angle) );
368 if(player->type == PL_TAILS && act->carrying && fabs(act->angle)<EPSILON) { float h=actor_image(act->carrying)->h, k=act->speed.y>5?h*0.7:0; downleft.y += k; downright.y += k; down.y += k; left.y += h*middle+random(h)-h*0.5; right.y = left.y; }
369 float cd_up[4] = { up.x-sqrsize-offx , up.y-sqrsize-offy , up.x+sqrsize-offx , up.y+sqrsize-offy };
370 float cd_down[4] = { down.x-sqrsize-offx , down.y-sqrsize-offy , down.x+sqrsize-offx , down.y+sqrsize-offy };
371 float cd_left[4] = { left.x-sqrsize-offx , left.y-sqrsize-offy , left.x+sqrsize-offx , left.y+sqrsize-offy };
372 float cd_right[4] = { right.x-sqrsize-offx , right.y-sqrsize-offy , right.x+sqrsize-offx , right.y+sqrsize-offy };
373 float cd_downleft[4] = { downleft.x-sqrsize-offx , downleft.y-sqrsize-offy , downleft.x+sqrsize-offx , downleft.y+sqrsize-offy };
374 float cd_downright[4] = { downright.x-sqrsize-offx , downright.y-sqrsize-offy , downright.x+sqrsize-offx , downright.y+sqrsize-offy };
375 float cd_upright[4] = { upright.x-sqrsize-offx , upright.y-sqrsize-offy , upright.x+sqrsize-offx , upright.y+sqrsize-offy };
376 float cd_upleft[4] = { upleft.x-sqrsize-offx , upleft.y-sqrsize-offy , upleft.x+sqrsize-offx , upleft.y+sqrsize-offy };
377 rectfill(video_get_backbuffer()->data, cd_up[0], cd_up[1], cd_up[2], cd_up[3], 0xFFFFFF);
378 rectfill(video_get_backbuffer()->data, cd_down[0], cd_down[1], cd_down[2], cd_down[3], 0xFFFFFF);
379 rectfill(video_get_backbuffer()->data, cd_left[0], cd_left[1], cd_left[2], cd_left[3], 0xFFFFFF);
380 rectfill(video_get_backbuffer()->data, cd_right[0], cd_right[1], cd_right[2], cd_right[3], 0xFFFFFF);
381 rectfill(video_get_backbuffer()->data, cd_downleft[0], cd_downleft[1], cd_downleft[2], cd_downleft[3], random(0xFFFFFF));
382 rectfill(video_get_backbuffer()->data, cd_downright[0], cd_downright[1], cd_downright[2], cd_downright[3], random(0xFFFFFF));
383 rectfill(video_get_backbuffer()->data, cd_upright[0], cd_upright[1], cd_upright[2], cd_upright[3], random(0xFFFFFF));
384 rectfill(video_get_backbuffer()->data, cd_upleft[0], cd_upleft[1], cd_upleft[2], cd_upleft[3], random(0xFFFFFF));
385 }
386 }
387 #endif
388 }
389
390
391
392 /*
393 * player_platform_movement()
394 * Platform movement. Returns
395 * a delta_space vector.
396 *
397 * Note: the actor's hot spot must
398 * be defined on its feet.
399 */
player_platform_movement(player_t * player,player_t * team[3],brick_list_t * brick_list,float gravity)400 v2d_t player_platform_movement(player_t *player, player_t *team[3], brick_list_t *brick_list, float gravity)
401 {
402 actor_t *act = player->actor;
403 const char *sprite_id = get_sprite_id(player->type);
404 float dt = timer_get_delta();
405 float max_y_speed = 480, friction = 0, gravity_factor = 1.0;
406 float maxspeed = act->maxspeed;
407 v2d_t ds = v2d_new(0,0);
408 int pushing_a_wall;
409 int angle_question;
410 int was_jumping = FALSE;
411 int is_walking = (player->actor->animation == sprite_get_animation(sprite_id, 1));
412 int at_right_border = FALSE, at_left_border = FALSE;
413 int climbing_a_slope = FALSE;
414 int block_tails_flight = FALSE;
415 animation_t *animation = NULL;
416
417 /* actor's collision detectors */
418 int frame_width = actor_image(act)->w, frame_height = actor_image(act)->h;
419 int slope = !((fabs(act->angle)<EPSILON)||(fabs(act->angle-PI/2)<EPSILON)||(fabs(act->angle-PI)<EPSILON)||(fabs(act->angle-3*PI/2)<EPSILON));
420 float diff = -2, sqrsize = 2, top=0, middle=0, lateral=0;
421 brick_t *brick_up, *brick_down, *brick_right, *brick_left;
422 brick_t *brick_upright, *brick_downright, *brick_downleft, *brick_upleft;
423 brick_t *brick_tmp;
424 v2d_t up, upright, right, downright, down, downleft, left, upleft;
425 v2d_t feet = act->position;
426 switch(player->type) {
427 case PL_SONIC:
428 if(!slope) { top = 0.7; middle = 0.5; lateral = 0.4; }
429 else { top = 1.0; middle = 0.8; lateral = 0.5; }
430 break;
431
432 case PL_TAILS:
433 if(!slope) { top = 0.7; middle = 0.5; lateral = 0.25; }
434 else { top = 1.0; middle = 0.7; lateral = 0.25; }
435 break;
436
437 case PL_KNUCKLES:
438 if(!slope) { top = 0.7; middle = 0.5; lateral = 0.25; }
439 else { top = 1.0; middle = 0.7; lateral = 0.25; }
440 break;
441 }
442 up = v2d_add ( feet , v2d_rotate( v2d_new(0, -frame_height*top+diff), -act->angle) );
443 down = v2d_add ( feet , v2d_rotate( v2d_new(0, -diff), -act->angle) );
444 left = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width*lateral+diff, -frame_height*middle), -act->angle) );
445 right = v2d_add ( feet , v2d_rotate( v2d_new(frame_width*lateral-diff, -frame_height*middle), -act->angle) );
446 upleft = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width*lateral+diff, -frame_height*top+diff), -act->angle) );
447 upright = v2d_add ( feet , v2d_rotate( v2d_new(frame_width*lateral-diff, -frame_height*top+diff), -act->angle) );
448 downleft = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width*lateral+diff, -diff), -act->angle) );
449 downright = v2d_add ( feet , v2d_rotate( v2d_new(frame_width*lateral-diff, -diff), -act->angle) );
450 if(player->type == PL_TAILS && act->carrying && fabs(act->angle)<EPSILON) { float h=actor_image(act->carrying)->h, k=act->speed.y>5?h*0.7:0; downleft.y += k; downright.y += k; down.y += k; left.y += h*middle+random(h)-h*0.5; right.y = left.y; }
451 actor_corners_disable_detection(player->disable_wall & PLAYER_WALL_LEFT, player->disable_wall & PLAYER_WALL_RIGHT, player->disable_wall & PLAYER_WALL_BOTTOM, player->disable_wall & PLAYER_WALL_TOP);
452 actor_corners_set_floor_priority( (player->disable_wall & PLAYER_WALL_BOTTOM) ? FALSE : TRUE );
453 actor_corners_ex(act, sqrsize, up, upright, right, downright, down, downleft, left, upleft, brick_list, &brick_up, &brick_upright, &brick_right, &brick_downright, &brick_down, &brick_downleft, &brick_left, &brick_upleft);
454 actor_corners_restore_floor_priority();
455
456 /* is the player dying? */
457 if(player->dying) {
458 act->speed.x = 0;
459 act->speed.y = min(max_y_speed, act->speed.y+gravity*dt);
460 act->mirror = IF_NONE;
461 act->angle = 0;
462 act->visible = TRUE;
463 player->blinking = FALSE;
464 player->death_timer += dt;
465 player->dead = (player->death_timer >= 2.5);
466 actor_change_animation(act, sprite_get_animation(sprite_id, 8));
467 return v2d_new(0, act->speed.y*dt + 0.5*gravity*dt*dt);
468 }
469 else if(player->dead)
470 return v2d_new(0,0);
471
472
473
474
475 /* clouds */
476 actor_handle_clouds(act, diff, &brick_up, &brick_upright, &brick_right, &brick_downright, &brick_down, &brick_downleft, &brick_left, &brick_upleft);
477
478
479
480 /* carry */
481 switch(player->type) {
482 case PL_SONIC: act->carry_offset = v2d_new((act->mirror&IF_HFLIP)?7:-9,-40); break;
483 case PL_TAILS: act->carry_offset = v2d_new((act->mirror&IF_HFLIP)?7:-7,-42); break;
484 case PL_KNUCKLES: act->carry_offset = v2d_new((act->mirror&IF_HFLIP)?7:-7,-42); break;
485 }
486
487 /* I'm being carried */
488 if(act->carried_by != NULL) {
489 player_t *host = NULL;
490 int i, host_id = 0, my_id = 0;
491 actor_t *car = act->carried_by;
492
493 /* my id? */
494 for(i=0; i<3; i++) {
495 if(team[i] == player) {
496 my_id = i;
497 break;
498 }
499 }
500
501 /* host = who is carrying me? */
502 for(i=0; i<3; i++) {
503 if(team[i]->actor == car) {
504 /* I've found the host! */
505 host = team[i];
506 host_id = i;
507
508 /* setting up some common flags... */
509 player->disable_wall = host->disable_wall;
510 player->entering_loop = host->entering_loop;
511 player->at_loopfloortop = host->at_loopfloortop;
512 player->bring_to_back = host->bring_to_back;
513
514 /* done */
515 break;
516 }
517 }
518
519 /* actions */
520 if(host && ((host->type == PL_TAILS && !host->flying) || host->getting_hit || host->dying || host->dead)) { /* what should host do? */
521 /* put me down! */
522 act->position = act->carried_by->position;
523 act->carried_by->carrying = NULL;
524 act->carried_by = NULL;
525 }
526 else if((brick_down && brick_down->brick_ref->angle == 0 && (int)car->speed.y >= 5) || player->getting_hit || player->dying || player->dead) { /* what should I do? */
527 /* put me down! */
528 act->position = act->carried_by->position;
529 act->carried_by->carrying = NULL;
530 act->carried_by = NULL;
531 }
532 else {
533 /* carry me! */
534 v2d_t offset = my_id < host_id ? v2d_multiply(car->speed, dt) : v2d_new(0,0);
535 act->speed = v2d_new(0,0);
536 act->mirror = car->mirror;
537 act->angle = 0;
538 actor_change_animation(act, sprite_get_animation(sprite_id, 25));
539 act->position = v2d_subtract(v2d_add(car->position, offset), act->carry_offset);
540 return v2d_new(0,0);
541 }
542 }
543
544
545
546
547 /* oh no, I got crushed! */
548 if(got_crushed(player, brick_up, brick_right, brick_down, brick_left)) {
549 player_kill(player);
550 return v2d_new(0,0);
551 }
552
553
554 /* speed shoes */
555 if(player->got_speedshoes) {
556 if(player->speedshoes_timer > PLAYER_MAX_SPEEDSHOES)
557 player->got_speedshoes = FALSE;
558 else {
559 maxspeed *= 1.5;
560 player->speedshoes_timer += dt;
561 }
562 }
563
564 /* if the player jumps inside a loop, enable the floor collision detection */
565 if(inside_loop(player)) {
566 if(act->is_jumping)
567 player->disable_wall &= ~PLAYER_WALL_BOTTOM;
568 }
569
570
571 /* disable spring mode */
572 if(player->spring) {
573 if((brick_down && (int)act->speed.y >= 0) || player->flying || player->climbing)
574 player->spring = FALSE;
575 }
576
577
578 /* useful flags */
579 pushing_a_wall = ((brick_right && input_button_down(act->input, IB_RIGHT)) || (brick_left && input_button_down(act->input, IB_LEFT))) && brick_down;
580 player->on_moveable_platform = (v2d_magnitude(level_brick_move_actor(brick_down,act)) > EPSILON);
581
582
583 /* wall collision */
584 climbing_a_slope = brick_down && ((act->angle > 0 && act->angle < PI/2 && act->speed.x>0) || (act->angle > 3*PI/2 && act->angle < 2*PI && act->speed.x<0));
585 if((climbing_a_slope && (brick_upleft || brick_upright)) || (fabs(act->angle) < EPSILON || fabs(act->angle-PI) < EPSILON)){
586 if(brick_right) {
587 if(brick_right->brick_ref->angle % 90 == 0 && (act->speed.x > EPSILON || right.x > brick_right->x)) {
588 if(!climbing_a_slope || (climbing_a_slope && brick_right->brick_ref->angle != 90)) {
589 act->speed.x = 0;
590 act->position.x = brick_right->x + (feet.x-right.x);
591 if(!act->is_jumping && !player->flying && !player->climbing && fabs(act->speed.y)<EPSILON)
592 animation = sprite_get_animation(sprite_id, pushing_a_wall ? 14 : 0);
593 if(climbing_a_slope) return v2d_new(-5,0);
594 }
595 }
596 }
597
598 if(brick_left) {
599 if(brick_left->brick_ref->angle % 90 == 0 && (act->speed.x < -EPSILON || left.x < brick_left->x+brick_left->brick_ref->image->w)) {
600 if(!climbing_a_slope || (climbing_a_slope && brick_left->brick_ref->angle != 270)) {
601 act->speed.x = 0;
602 act->position.x = (brick_left->x+brick_left->brick_ref->image->w) + (feet.x-left.x);
603 if(!act->is_jumping && !player->flying && !player->climbing && fabs(act->speed.y)<EPSILON)
604 animation = sprite_get_animation(sprite_id, pushing_a_wall ? 14 : 0);
605 if(climbing_a_slope) return v2d_new(5,0);
606 }
607 }
608 }
609
610 if(act->position.x <= act->hot_spot.x) {
611 player->spin = FALSE;
612 at_left_border = TRUE;
613
614 if(act->position.x < act->hot_spot.x) {
615 act->speed.x = 0;
616 act->position.x = act->hot_spot.x;
617 if(brick_down) {
618 pushing_a_wall = TRUE;
619 animation = sprite_get_animation(sprite_id, 1);
620 }
621 }
622 }
623
624 if(act->position.x >= level_size().x - (actor_image(act)->w - act->hot_spot.x)) {
625 player->spin = FALSE;
626 at_right_border = TRUE;
627
628 if(act->position.x > level_size().x - (actor_image(act)->w - act->hot_spot.x)) {
629 act->speed.x = 0;
630 act->position.x = level_size().x - (actor_image(act)->w - act->hot_spot.x);
631 if(brick_down) {
632 pushing_a_wall = TRUE;
633 animation = sprite_get_animation(sprite_id, 1);
634 }
635 }
636 }
637 }
638
639
640 /* y-axis */
641 stickyphysics_hack(player, brick_list, &brick_downleft, &brick_down, &brick_downright);
642 if(!player->climbing) {
643 if(brick_down) {
644 int ang = brick_down->brick_ref->angle;
645 int spin_block;
646 float factor, jump_sensitivity = 1.0;
647 was_jumping = TRUE;
648 act->ignore_horizontal = FALSE;
649 player->is_fire_jumping = FALSE;
650 act->is_jumping = FALSE;
651
652 /* falling bricks? */
653 if(brick_down->brick_ref && brick_down->brick_ref->behavior == BRB_FALL && brick_down->state == BRS_IDLE)
654 brick_down->state = BRS_ACTIVE;
655
656 /* stopped, walking, running, spinning... */
657 if(fabs(act->speed.x) < EPSILON) {
658 if(ang%180==0) player->spin = FALSE;
659
660 /* look down */
661 if(input_button_down(act->input, IB_DOWN)) {
662 /* crouch down */
663 if(!player->spin_dash)
664 animation = sprite_get_animation(sprite_id, 4);
665
666 /* spin dash - start */
667 if(input_button_pressed(act->input, IB_FIRE1)) {
668 animation = sprite_get_animation(sprite_id, 6);
669 player->spin_dash = TRUE;
670 sound_play( soundfactory_get("charge") );
671 }
672 }
673 else if(!pushing_a_wall) {
674 if(input_button_down(act->input, IB_UP)) { /* look up */
675 if(!(is_walking && player->at_some_border))
676 animation = sprite_get_animation(sprite_id, 5);
677 }
678 else if(!inside_loop(player)) {
679 /* stopped / ledge */
680 brick_t *minileft, *miniright;
681 v2d_t vminileft = v2d_add ( feet , v2d_rotate( v2d_new(-8, 0), -act->angle) );
682 v2d_t vminiright = v2d_add ( feet , v2d_rotate( v2d_new(5, 0), -act->angle) );
683 v2d_t v = v2d_new(0,0);
684 actor_corners_ex(act, sqrsize, v, v, v, vminiright, v, vminileft, v, v, brick_list, NULL, NULL, NULL, &miniright, NULL, &minileft, NULL, NULL);
685 if(((!miniright && !(act->mirror&IF_HFLIP)) || (!minileft && (act->mirror&IF_HFLIP))) && !player->on_moveable_platform)
686 animation = sprite_get_animation(sprite_id, 10);
687 else {
688 if( !((input_button_down(act->input, IB_LEFT) && (at_left_border || player->at_some_border)) || (input_button_down(act->input, IB_RIGHT) && (at_right_border || player->at_some_border))) )
689 animation = sprite_get_animation(sprite_id, 0);
690 else {
691 act->mirror = at_left_border ? IF_HFLIP : IF_NONE;
692 animation = sprite_get_animation(sprite_id, 1);
693 }
694 }
695 }
696 else /* stopped */
697 animation = sprite_get_animation(sprite_id, 0);
698 }
699
700 /* spin dash */
701 if(player->spin_dash) {
702
703 /* particles */
704 int a, sd_sig = act->mirror&IF_HFLIP ? 1 : -1, r;
705 v2d_t sd_relativepos, sd_speed;
706 image_t *pixel;
707
708 for(a=0; a<3; a++) {
709 r = 128+random(128);
710 pixel = image_create(1,1);
711 image_clear(pixel, image_rgb(r,r,r));
712
713 sd_relativepos = v2d_new(sd_sig*(7+random(7)), 2);
714 sd_speed = v2d_new(sd_sig * (50+random(200)), -random(200));
715
716 level_create_particle(pixel, v2d_add(act->position,sd_relativepos), sd_speed, TRUE);
717 }
718
719 /* end */
720 if(input_button_up(act->input, IB_DOWN) || level_editmode()) {
721 player->spin = TRUE;
722 player->spin_dash = FALSE;
723 if( ((act->mirror&IF_HFLIP)&&!brick_left&&!at_left_border) || (!(act->mirror&IF_HFLIP)&&!brick_right&&!at_right_border) )
724 act->speed.x = ( act->mirror & IF_HFLIP ? -1 : 1 )*maxspeed*1.35;
725 sound_play( soundfactory_get("release") );
726 player->disable_jump_for = 0.05; /* disable jumping for how long? */
727 }
728 }
729
730
731 }
732 else {
733 if(input_button_down(act->input, IB_DOWN)) {
734 if(!player->spin)
735 sound_play( soundfactory_get("roll") );
736 player->spin = TRUE;
737 }
738
739
740 if(!player->spin && !player->braking) {
741 float max_walking_speed = maxspeed * 0.75;
742 float min_braking_speed = maxspeed * 0.35;
743
744 /* animation */
745 if(fabs(act->speed.x) < max_walking_speed) {
746 if(!pushing_a_wall && act->speed.y >= 0) {
747 animation = sprite_get_animation(sprite_id, 1); /* walking animation */
748 actor_change_animation_speed_factor(act, 0.5 + 1.5*(fabs(act->speed.x) / max_walking_speed)); /* animation speed */
749 }
750 }
751 else
752 animation = sprite_get_animation(sprite_id, 2); /* running animation */
753
754 /* brake */
755 if(fabs(act->speed.x) >= min_braking_speed) {
756 if( (input_button_down(act->input, IB_RIGHT)&&(act->speed.x<0)) || (input_button_down(act->input, IB_LEFT)&&(act->speed.x>0)) ) {
757 sound_play( soundfactory_get("brake") );
758 player->braking = TRUE;
759 }
760 }
761
762 }
763 else if(player->spin)
764 animation = sprite_get_animation(sprite_id, 3); /* spinning */
765 else if(player->braking) {
766 /* particles */
767 int r, sd_sig = act->mirror&IF_HFLIP ? 1 : -1;
768 v2d_t sd_relativepos, sd_speed;
769 image_t *pixel;
770
771 r = 128+random(128);
772 pixel = image_create(1,1);
773 image_clear(pixel, image_rgb(r,r,r));
774 sd_relativepos = v2d_new(sd_sig*(10-random(21)), 0);
775 sd_speed = v2d_new(sd_sig * (50+random(200)), -random(200));
776 level_create_particle(pixel, v2d_add(act->position,sd_relativepos), sd_speed, TRUE);
777
778 /* braking */
779 animation = sprite_get_animation(sprite_id, 7);
780 if(fabs(act->speed.x)<10) player->braking = FALSE;
781 }
782 }
783
784 /* disable jump? */
785 player->disable_jump_for = max(player->disable_jump_for - dt, 0.0);
786 if(fabs(act->speed.x) < EPSILON)
787 player->disable_jump_for = 0.0;
788
789 /* jump */
790 spin_block = !player->spin_dash;
791 if(input_button_down(act->input, IB_FIRE1) && (player->disable_jump_for <= 0.0) && !input_button_down(act->input, IB_DOWN) && !brick_up && !player->landing && spin_block && !act->is_jumping) {
792 if(act->speed.y >= 0 && (player->type != PL_KNUCKLES || (player->type == PL_KNUCKLES && !player->flying)))
793 sound_play( soundfactory_get("jump") );
794 act->angle = NATURAL_ANGLE;
795 act->is_jumping = TRUE;
796 player->is_fire_jumping = TRUE;
797 block_tails_flight = TRUE;
798 player->spin = FALSE;
799 animation = sprite_get_animation(sprite_id, 3);
800 if(ang == 0) {
801 act->speed.y = (-act->jump_strength) * jump_sensitivity;
802 }
803 else if(ang > 0 && ang < 90) {
804 if(ang > 45) {
805 act->speed.x = min(act->speed.x, (-0.7*act->jump_strength) * jump_sensitivity);
806 act->speed.y = (-0.7*act->jump_strength) * jump_sensitivity;
807 }
808 else {
809 act->speed.x *= act->speed.x > 0 ? 0.5 : 1.0;
810 act->speed.y = (-act->jump_strength) * jump_sensitivity;
811 }
812 }
813 else if(ang == 90) {
814 actor_move(act, v2d_new(20*diff, 0));
815 act->speed.x = min(act->speed.x, (-act->jump_strength) * jump_sensitivity);
816 act->speed.y = (-act->jump_strength/2) * jump_sensitivity;
817 }
818 else if(ang > 90 && ang < 180) {
819 actor_move(act, v2d_new(0, -20*diff));
820 act->speed.x = min(act->speed.x, (-0.7*act->jump_strength) * jump_sensitivity);
821 act->speed.y = (act->jump_strength) * jump_sensitivity;
822 }
823 else if(ang == 180) {
824 actor_move(act, v2d_new(0, -20*diff));
825 act->speed.x *= -1;
826 act->speed.y = (act->jump_strength) * jump_sensitivity;
827 }
828 else if(ang > 180 && ang < 270) {
829 actor_move(act, v2d_new(0, -20*diff));
830 act->speed.x = max(act->speed.x, (0.7*act->jump_strength) * jump_sensitivity);
831 act->speed.y = (act->jump_strength) * jump_sensitivity;
832 }
833 else if(ang == 270) {
834 actor_move(act, v2d_new(-20*diff, 0));
835 act->speed.x = max(act->speed.x, (act->jump_strength) * jump_sensitivity);
836 act->speed.y = (-act->jump_strength/2) * jump_sensitivity;
837 }
838 else if(ang > 270 && ang < 360) {
839 if(ang < 315) {
840 act->speed.x = max(act->speed.x, (0.7*act->jump_strength) * jump_sensitivity);
841 act->speed.y = (-0.7*act->jump_strength) * jump_sensitivity;
842 }
843 else {
844 act->speed.x *= act->speed.x < 0 ? 0.5 : 1.0;
845 act->speed.y = (-act->jump_strength) * jump_sensitivity;
846 }
847 }
848 }
849
850
851 /* slopes / speed issues */
852 if(!act->is_jumping) {
853 float mytan, super = 1.2, push = 25.0;
854 if(ang > 0 && ang < 90) {
855 mytan = min(1, tan( ang*PI/180.0 ))*0.8;
856 if(fabs(act->speed.y) > EPSILON)
857 act->speed.x = (was_jumping && ang<=45) ? act->speed.x : max(-super*maxspeed, -1*mytan*act->speed.y);
858 else {
859 factor = (!(act->mirror & IF_HFLIP) ? 1.0 : 2.0) * mytan;
860 if(player->braking && ang<45)
861 factor *= 8.0 * (act->speed.x<0 ? -1.0/2.0 : 1.0/1.0);
862 else if(fabs(act->speed.x)<5) {
863 factor *= sin(ang*PI/180.0)*push;
864 player->lock_accel = LOCKACCEL_RIGHT;
865 }
866 act->speed.x = max(act->speed.x - factor*700*dt, -super*maxspeed);
867 }
868 }
869 else if(ang > 270 && ang < 360) {
870 mytan = min(1, -tan( ang*PI/180.0 ))*0.8;
871 if(fabs(act->speed.y) > EPSILON)
872 act->speed.x = (was_jumping && ang>=315) ? act->speed.x : min(super*maxspeed, 1*mytan*act->speed.y);
873 else {
874 factor = ((act->mirror & IF_HFLIP) ? 1.0 : 2.0) * mytan;
875 if(player->braking && ang>315)
876 factor *= 8.0 * (act->speed.x>0 ? -1.0/2.0 : 1.0/1.0);
877 else if(fabs(act->speed.x)<5) {
878 factor *= -sin(ang*PI/180.0)*push;
879 player->lock_accel = LOCKACCEL_LEFT;
880 }
881 act->speed.x = min(act->speed.x + factor*700*dt, super*maxspeed);
882 }
883 }
884 }
885
886 if(ang%90 == 0)
887 player->lock_accel = LOCKACCEL_NONE;
888
889 if(brick_downleft && brick_downright && fabs(act->speed.x) < 40) {
890 if(brick_downleft->brick_ref->angle > 270 && brick_downleft->brick_ref->angle < 360 && brick_downright->brick_ref->angle > 0 && brick_downright->brick_ref->angle < 90) {
891 if(!input_button_down(act->input, IB_LEFT) && !input_button_down(act->input, IB_RIGHT))
892 act->speed.x = 0;
893 }
894 }
895 }
896 else { /* not brick_down */
897 player->braking = FALSE;
898 player->lock_accel = LOCKACCEL_NONE;
899
900 if(player->spin_dash) {
901 player->spin_dash = FALSE;
902 animation = sprite_get_animation(sprite_id, 1);
903 }
904
905 if(act->animation == sprite_get_animation(sprite_id, 0) || act->animation == sprite_get_animation(sprite_id, 10) || act->animation == sprite_get_animation(sprite_id, 5))
906 animation = sprite_get_animation(sprite_id, 1);
907
908 if(player->spring || is_walking || act->speed.y < 0)
909 player->spin = FALSE;
910
911 if(!inside_loop(player))
912 act->angle = NATURAL_ANGLE;
913 }
914
915
916 /* jump sensitivity */
917 if(!brick_down) {
918 if(player->is_fire_jumping && act->speed.y < -act->jump_strength*PLAYER_JUMP_SENSITIVITY) {
919 if(input_button_up(act->input, IB_FIRE1))
920 act->speed.y *= 0.7;
921 }
922 }
923
924 /* who can fly? */
925 if(player->type == PL_TAILS && player->flying) {
926 gravity_factor = (player->flight_timer < TAILS_MAX_FLIGHT) ? 0.15 : 0.8;
927 max_y_speed *= 0.3;
928 }
929 else
930 gravity_factor = 1.0;
931
932 /* y-axis movement */
933 ds.y = (fabs(act->speed.y) > EPSILON) ? act->speed.y*dt + 0.5*(gravity*gravity_factor)*(dt*dt) : 0;
934 if(!(player->type == PL_KNUCKLES && player->flying))
935 act->speed.y = min(act->speed.y + (gravity*gravity_factor)*dt, max_y_speed);
936
937
938
939
940 /* ceiling collision */
941 angle_question = (brick_up && brick_up->brick_ref->angle%90!=0) && fabs(act->angle)<EPSILON;
942 if(brick_up && (brick_up->brick_ref->angle % 90 == 0 || angle_question) && act->speed.y < -EPSILON) {
943 act->position.y = (brick_up->y+brick_up->brick_ref->image->h) + (feet.y-up.y);
944 act->speed.y = 10;
945
946 /* this is a moving brick... and it's moving down */
947 if(brick_up->brick_ref->behavior == BRB_CIRCULAR) {
948 if(sin(brick_up->brick_ref->behavior_arg[3] * brick_up->value[0]) > 0) {
949 act->speed.y = 100;
950 ds = v2d_add(ds, v2d_multiply(level_brick_move_actor(brick_up, act), dt));
951 return ds;
952 }
953 }
954 }
955
956
957
958 /* floor collision */
959 brick_tmp = brick_down;
960 if(brick_tmp && !act->is_jumping) {
961 int ang = brick_tmp->brick_ref->angle;
962 act->speed.y = ds.y = 0;
963 act->angle = ang * PI / 180.0;
964
965 /* 0 floor */
966 if(ang == 0) {
967 v2d_t mov = level_brick_move_actor(brick_down, act); /* moveable platforms I */
968 feet.y = brick_tmp->y;
969 friction = 0;
970 if(mov.y > EPSILON) /* if the moveable brick is going down... */
971 ds.y += mov.y*dt;
972 else
973 act->position.y = feet.y+diff+1;
974 }
975
976 /* (0-90) slope */
977 else if(ang > 0 && ang < 90) {
978 feet.y = brick_tmp->y + brick_tmp->brick_ref->image->h - (act->position.x-brick_tmp->x)*tan(act->angle);
979 if(act->speed.x<0) feet.y += 2.0;
980 act->position.y = feet.y+diff;
981 if(!(act->mirror & IF_HFLIP)) friction = 0.2;
982 }
983
984 /* 90 wall */
985 else if(ang == 90) {
986 if(fabs(act->speed.x) > 5) {
987 int myang = brick_downright ? brick_downright->brick_ref->angle : -1;
988 if(brick_downright && (myang >= ang && myang < ang+90)) {
989 feet.y = brick_tmp->x;
990 if(!player->flying) act->position.x = feet.y+diff;
991 }
992 else {
993 act->angle = NATURAL_ANGLE;
994 act->is_jumping = TRUE;
995 if(!player->spin && !player->flying) animation = sprite_get_animation(sprite_id, 1);
996 if(!inside_loop(player)) {
997 if(!player->flying) actor_move(act, v2d_new(6.5*diff, 0));
998 act->speed = v2d_new(0, -0.9*fabs(act->speed.x));
999 }
1000 }
1001 }
1002 else {
1003 act->angle = NATURAL_ANGLE;
1004 if(!player->flying) actor_move(act, v2d_new(5*diff, 0));
1005 act->is_jumping = TRUE;
1006 act->ignore_horizontal = FALSE;
1007 }
1008 if(!(act->mirror & IF_HFLIP)) friction = 1.5;
1009 }
1010
1011 /* (90-180) slope */
1012 else if(ang > 90 && ang < 180) {
1013 if(fabs(act->speed.x) > 5) {
1014 feet.y = brick_tmp->y - (act->position.x-brick_tmp->x)*tan(act->angle);
1015 act->position.y = feet.y-diff;
1016 }
1017 else {
1018 act->angle = NATURAL_ANGLE;
1019 actor_move(act, v2d_new(0, -15*diff));
1020 act->is_jumping = TRUE;
1021 }
1022 friction = 1.5;
1023 }
1024
1025 /* 180 ceiling */
1026 else if(ang == 180) {
1027 if(fabs(act->speed.x) > 5) {
1028 feet.y = brick_tmp->y + brick_tmp->brick_ref->image->h;
1029 act->position.y = feet.y-diff;
1030
1031 /* end of ceil */
1032 if( (act->speed.x > 0 && !brick_downright) || (act->speed.x < 0 && !brick_downleft) ) {
1033 actor_move(act, v2d_new(0, 15*diff));
1034 act->is_jumping = TRUE;
1035 act->speed.x *= -1;
1036 act->mirror = act->speed.x<0 ? IF_HFLIP : IF_NONE;
1037 act->angle = NATURAL_ANGLE;
1038 }
1039 }
1040 else {
1041 act->angle = NATURAL_ANGLE;
1042 actor_move(act, v2d_new(0, -20*diff));
1043 act->is_jumping = TRUE;
1044 act->speed.x = 0;
1045 }
1046 friction = 1.2;
1047 }
1048
1049 /* (180-270) slope */
1050 else if(ang > 180 && ang < 270) {
1051 if(fabs(act->speed.x) > 5) {
1052 feet.y = brick_tmp->y + brick_tmp->brick_ref->image->h - (act->position.x-brick_tmp->x)*tan(act->angle);
1053 act->position.y = feet.y-diff;
1054 }
1055 else {
1056 act->angle = NATURAL_ANGLE;
1057 actor_move(act, v2d_new(0, -15*diff));
1058 act->is_jumping = TRUE;
1059 }
1060 friction = 1.5;
1061 }
1062
1063 /* 270 wall */
1064 else if(ang == 270) {
1065 if(fabs(act->speed.x) > 5) {
1066 int myang = brick_downleft ? brick_downleft->brick_ref->angle : -1;
1067 if(brick_downleft && (myang > ang-90 && myang <= ang)) {
1068 feet.y = brick_tmp->x + brick_tmp->brick_ref->image->w;
1069 if(!player->flying) act->position.x = feet.y-diff;
1070 }
1071 else {
1072 act->angle = NATURAL_ANGLE;
1073 act->is_jumping = TRUE;
1074 if(!player->spin && !player->flying) animation = sprite_get_animation(sprite_id, 1);
1075 if(!inside_loop(player)) {
1076 if(!player->flying) actor_move(act, v2d_new(-6.5*diff, 0));
1077 act->speed = v2d_new(0, -0.9*fabs(act->speed.x));
1078 }
1079 }
1080
1081 }
1082 else {
1083 act->angle = NATURAL_ANGLE;
1084 if(!player->flying) actor_move(act, v2d_new(-5*diff, 0));
1085 act->is_jumping = TRUE;
1086 act->ignore_horizontal = FALSE;
1087 }
1088 if(act->mirror & IF_HFLIP) friction = 1.5;
1089 }
1090
1091 /* (270-360) slope */
1092 else if(ang > 270 && ang < 360) {
1093 feet.y = brick_tmp->y - (act->position.x-brick_tmp->x)*tan(act->angle);
1094 if(act->speed.x>0) feet.y += 2.0;
1095 act->position.y = feet.y+diff;
1096 if(act->mirror & IF_HFLIP) friction = 0.2;
1097 }
1098 }
1099
1100
1101 /* x-axis */
1102 ds.x = (fabs(act->speed.x) > EPSILON) ? act->speed.x*dt + 0.5*((1.0-friction)*act->acceleration)*(dt*dt) : 0;
1103 if(input_button_down(act->input, IB_LEFT) && !input_button_down(act->input, IB_RIGHT) && !player->spin && !player->braking && !player->landing && !player->getting_hit && player->lock_accel != LOCKACCEL_LEFT && !at_left_border) {
1104 if(!act->ignore_horizontal && (act->is_jumping || player->spring || is_walking || !input_button_down(act->input, IB_DOWN))) {
1105 act->mirror = IF_HFLIP;
1106 friction = (act->speed.x > 0) ? -1.0 : friction;
1107 if(act->speed.x >= -maxspeed*1.1)
1108 act->speed.x = max(act->speed.x - (1.0-friction)*act->acceleration*dt, -maxspeed);
1109 }
1110 }
1111 else if(input_button_down(act->input, IB_RIGHT) && !input_button_down(act->input, IB_LEFT) && !player->spin && !player->braking && !player->landing && !player->getting_hit && player->lock_accel != LOCKACCEL_RIGHT && !at_right_border) {
1112 if(!act->ignore_horizontal && (act->is_jumping || player->spring || is_walking || !input_button_down(act->input, IB_DOWN))) {
1113 act->mirror = IF_NONE;
1114 friction = (act->speed.x < 0) ? -1.0 : friction;
1115 if(act->speed.x <= maxspeed*1.1)
1116 act->speed.x = min(act->speed.x + (1.0-friction)*act->acceleration*dt, maxspeed);
1117 }
1118 }
1119 else if(brick_down) {
1120 int signal = 0;
1121 int ang = brick_down->brick_ref->angle;
1122 float factor;
1123
1124 /* deceleration factor */
1125 if(player->spin)
1126 factor = 0.65;
1127 else if(player->braking)
1128 factor = 4.5;
1129 else if(player->landing)
1130 factor = 0.6;
1131 else
1132 factor = 1.0;
1133
1134 /* deceleration */
1135 if(ang % 90 == 0) {
1136 if(ang == 90)
1137 signal = -1;
1138 else if(ang == 270)
1139 signal = 1;
1140 else {
1141 if(act->speed.x > EPSILON) signal = -1;
1142 else if(-act->speed.x > EPSILON) signal = 1;
1143 else signal = 0;
1144 }
1145 }
1146 else if((ang > 90 && ang < 180) || (ang > 180 && ang < 270)){
1147 if(act->speed.x > EPSILON) signal = -1;
1148 else if(-act->speed.x > EPSILON) signal = 1;
1149 else signal = 0;
1150 }
1151
1152 act->speed.x += signal*factor*act->acceleration*dt;
1153 }
1154 }
1155
1156
1157 /* spring mode */
1158 if(player->spring) {
1159 animation = sprite_get_animation(sprite_id, act->speed.y <= 0 ? 13 : 1);
1160 if(act->speed.y > 0) {
1161 player->spring = FALSE;
1162 act->is_jumping = FALSE;
1163 }
1164 }
1165
1166
1167 /* got hurt? */
1168 if(player->getting_hit) {
1169 if(!brick_down)
1170 animation = sprite_get_animation(sprite_id, 11);
1171 else
1172 player->getting_hit = FALSE;
1173 }
1174
1175
1176
1177 /* character's specific routines */
1178 switch(player->type) {
1179 case PL_SONIC:
1180 break;
1181
1182 case PL_TAILS:
1183 /* tails can fly */
1184 player->flight_timer += dt;
1185 if(brick_down && brick_down->brick_ref->angle != 90 && brick_down->brick_ref->angle != 270) { player->flying = FALSE; player->flight_timer = 0; }
1186 if(((act->is_jumping && act->speed.y>-act->jump_strength/3 && !block_tails_flight && !player->getting_hit) || player->flying) && input_button_pressed(act->input, IB_FIRE1) && !player->getting_hit) {
1187 if(player->flight_timer < TAILS_MAX_FLIGHT) {
1188 if(!player->flying) player->flight_timer = 0;
1189 act->speed.y = -level_gravity()*0.1;
1190 player->flying = TRUE;
1191 act->is_jumping = FALSE;
1192 player->is_fire_jumping = FALSE;
1193 }
1194 }
1195 if(player->flying) {
1196 animation = sprite_get_animation(sprite_id, act->carrying ? 16 : 20);
1197 act->speed.x = clip(act->speed.x, -act->maxspeed/2, act->maxspeed/2);
1198 if(player->flight_timer >= TAILS_MAX_FLIGHT) {
1199 /* i'm tired of flying... */
1200 sound_t *smp = soundfactory_get("tired of flying");
1201 if(!sound_is_playing(smp)) sound_play(smp);
1202 animation = sprite_get_animation(sprite_id, 19);
1203 }
1204 else {
1205 sound_t *smp;
1206 int i;
1207
1208 /* i'm flying! :) */
1209 if(inside_loop(player)) act->angle = NATURAL_ANGLE;
1210 smp = soundfactory_get("flying");
1211 if(!sound_is_playing(smp)) sound_play(smp);
1212
1213 /* pick up: let's carry someone... */
1214 for(i=0; i<3 && act->carrying == NULL; i++) {
1215 if(team[i] != player && (int)act->speed.y <= 0) {
1216 float ra[4] = { team[i]->actor->position.x+actor_image(team[i]->actor)->w*0.3, team[i]->actor->position.y, team[i]->actor->position.x+actor_image(team[i]->actor)->w*0.7, team[i]->actor->position.y+actor_image(team[i]->actor)->h*0.2 };
1217 float rb[4] = { act->position.x+actor_image(act)->w*0.3, act->position.y+actor_image(act)->h*0.7, act->position.x+actor_image(act)->w*0.7, act->position.y+actor_image(act)->h };
1218 int collision = bounding_box(ra, rb);
1219 int can_be_carried = (team[i]->actor->carried_by == NULL && !team[i]->dying && !team[i]->dead && !team[i]->climbing && !team[i]->landing && !team[i]->getting_hit);
1220 if(collision && can_be_carried && !brick_down) {
1221 act->carrying = team[i]->actor;
1222 team[i]->actor->carried_by = act;
1223 team[i]->spin = team[i]->spin_dash = team[i]->braking = team[i]->flying = team[i]->spring = team[i]->on_moveable_platform = FALSE;
1224 sound_play( soundfactory_get("touch the wall") );
1225 }
1226 }
1227 }
1228 }
1229 }
1230 else if(act->animation == sprite_get_animation(sprite_id, act->carrying ? 16 : 20))
1231 animation = sprite_get_animation(sprite_id, 1); /* if you're not flying, don't play the flying animation */
1232
1233 break;
1234
1235 case PL_KNUCKLES:
1236 /* knuckles can fly too! */
1237 if(((act->is_jumping && act->speed.y>-0.7*act->jump_strength) || player->flying) && input_button_pressed(act->input, IB_FIRE1) && !brick_down && !player->getting_hit) {
1238 act->speed.y = 50;
1239 player->flying = TRUE;
1240 act->is_jumping = FALSE;
1241 player->is_fire_jumping = FALSE;
1242 act->speed.x = (act->mirror & IF_HFLIP) ? min(-100, act->speed.x) : max(100, act->speed.x);
1243 }
1244
1245
1246 /* fly? */
1247 if(player->flying) {
1248 int turning = (input_button_down(act->input, IB_LEFT) && act->speed.x > 0) || (input_button_down(act->input, IB_RIGHT) && act->speed.x < 0);
1249 int floor = (brick_down && fabs(brick_down->brick_ref->angle*PI/180.0 - NATURAL_ANGLE) < EPSILON);
1250 turning += (act->animation == sprite_get_animation(sprite_id, 21)) && !actor_animation_finished(act);
1251
1252 /* i'm flying... */
1253 if(!floor && act->animation != sprite_get_animation(sprite_id, 19) && !player->landing) {
1254 if(!(act->mirror & IF_HFLIP)) {
1255 animation = sprite_get_animation(sprite_id, turning ? 21 : 20);
1256 act->speed.x = min(act->speed.x + (0.5*act->acceleration)*dt, maxspeed/2);
1257 }
1258 else {
1259 animation = sprite_get_animation(sprite_id, turning ? 21 : 20);
1260 act->speed.x = max(act->speed.x - (0.5*act->acceleration)*dt, -maxspeed/2);
1261 }
1262 }
1263
1264 /* end of flight */
1265 if(floor) {
1266 /* collided with the floor */
1267 player->landing = TRUE;
1268 act->is_jumping = FALSE;
1269 animation = sprite_get_animation(sprite_id, 19);
1270 act->speed.y = 0; ds.y = 0;
1271 player->climbing = FALSE;
1272 }
1273 else if(input_button_up(act->input, IB_FIRE1)) {
1274 /* knuckles doesn't want to fly anymore */
1275 player->flying = FALSE;
1276 animation = sprite_get_animation(sprite_id, 18);
1277 }
1278 else {
1279 int t;
1280 brick_t *try_me[5] = { brick_left , brick_downleft , brick_right , brick_downright , brick_down };
1281 for(t=0; t<5; t++) {
1282 brick_tmp = try_me[t];
1283 if(brick_tmp && brick_tmp->brick_ref->angle%90!=0) {
1284 /* collided with a slope while flying? */
1285 player->flying = FALSE;
1286 player->landing = FALSE;
1287 }
1288 }
1289 }
1290
1291 /* wall climbing - begin */
1292 if(!floor && !brick_up) {
1293 if((brick_left && brick_left->brick_ref->angle%90==0) || (brick_right && brick_right->brick_ref->angle%90==0)) {
1294 player->climbing = TRUE;
1295 player->flying = FALSE;
1296 sound_play( soundfactory_get("touch the ground") );
1297 }
1298 }
1299 }
1300
1301
1302 /* no more landing */
1303 if(player->landing) {
1304 if(fabs(act->speed.x) < EPSILON || !brick_down)
1305 player->flying = player->landing = FALSE;
1306 }
1307
1308
1309 /* wall climbing */
1310 if(player->climbing) {
1311 v2d_t pre_ds = v2d_new(0,0);
1312 act->speed.x = ds.x = 0;
1313 if(brick_left && !brick_right) act->mirror |= IF_HFLIP;
1314 if(brick_right && !brick_left) act->mirror &= ~IF_HFLIP;
1315
1316 /* climbing a moving brick */
1317 pre_ds = v2d_add(pre_ds, v2d_multiply(level_brick_move_actor(brick_left, act), dt));
1318 pre_ds = v2d_add(pre_ds, v2d_multiply(level_brick_move_actor(brick_right, act), dt));
1319 if((pre_ds.y <= 0 && !brick_up) || (pre_ds.y >= 0 && !brick_down) || (!brick_left && brick_right))
1320 ds = v2d_add(ds, pre_ds);
1321
1322 /* climbing... */
1323 if(brick_left || brick_right) {
1324
1325 /* knuckles doesn't want to climb the wall anymore */
1326 if(input_button_pressed(act->input, IB_FIRE1)) {
1327 animation_t *an_a = sprite_get_animation(sprite_id, 17);
1328 animation_t *an_b = sprite_get_animation(sprite_id, 22);
1329 if(act->animation == an_a || act->animation == an_b) { /* no wall kicking */
1330 player->climbing = FALSE;
1331 act->is_jumping = TRUE;
1332 player->is_fire_jumping = TRUE;
1333 act->speed.x = ((act->mirror&IF_HFLIP)?1:-1)*0.7*act->jump_strength;
1334 act->speed.y = -0.5*act->jump_strength;
1335 if(brick_left && !brick_right) act->mirror &= ~IF_HFLIP;
1336 if(!brick_left && brick_right) act->mirror |= IF_HFLIP;
1337 animation = sprite_get_animation(sprite_id, 3);
1338 sound_play( soundfactory_get("jump") );
1339 }
1340 }
1341 else {
1342 /* up or down? */
1343 if(input_button_down(act->input, IB_UP)) {
1344 if(!brick_up) {
1345 ds.y = (-maxspeed*0.1) * dt;
1346 animation = sprite_get_animation(sprite_id, 17);
1347 }
1348 }
1349 else if(input_button_down(act->input, IB_DOWN)) {
1350 if(!brick_down) {
1351 ds.y = (maxspeed*0.1) * dt;
1352 animation = sprite_get_animation(sprite_id, 17);
1353 }
1354 else
1355 player->climbing = FALSE; /* reached the ground */
1356 }
1357 else
1358 animation = sprite_get_animation(sprite_id, 22);
1359 }
1360 }
1361
1362 /* end of wall climbing */
1363 else {
1364 brick_tmp = (act->mirror&IF_HFLIP) ? brick_downleft : brick_downright;
1365 if(brick_tmp) {
1366 animation = sprite_get_animation(sprite_id, 23);
1367 act->ignore_horizontal = TRUE;
1368 ds = v2d_add(ds, v2d_multiply(level_brick_move_actor(brick_tmp, act), dt));
1369 if(actor_animation_finished(act)) {
1370 player->climbing = FALSE;
1371 act->ignore_horizontal = FALSE;
1372 act->speed = v2d_new(((act->mirror&IF_HFLIP)?-1:1)*maxspeed*0.15, -level_gravity()/12.5);
1373 ds.x = ((act->mirror&IF_HFLIP)?-1:1)*5;
1374 }
1375 }
1376 else {
1377 player->climbing = FALSE;
1378 act->is_jumping = TRUE;
1379 animation = sprite_get_animation(sprite_id, 3);
1380 }
1381 }
1382
1383
1384 }
1385 break;
1386 }
1387
1388
1389 /* almost done... */
1390 player->at_some_border = FALSE;
1391 if(animation) actor_change_animation(act, animation);
1392 if(fabs(act->speed.x) < 4) {
1393 player->braking = FALSE;
1394 if( (!input_button_down(act->input, IB_RIGHT) && !input_button_down(act->input, IB_LEFT)) || (input_button_down(act->input, IB_RIGHT) && input_button_down(act->input, IB_LEFT)) || player->spin || player->landing ) {
1395 ds.x = 0;
1396 act->speed.x = 0;
1397 }
1398 }
1399 ds.x += level_brick_move_actor(brick_down,act).x*dt; /* moveable platforms II */
1400 ds.x = ((act->position.x<=act->hot_spot.x) && act->speed.x<0) ? 0 : ds.x;
1401 ds.x = ((act->position.x>=level_size().x - (actor_image(act)->w-act->hot_spot.x)) && act->speed.x>0) ? 0 : ds.x;
1402 return ds;
1403 }
1404
1405
1406 /*
1407 * player_bounce()
1408 * Bounces
1409 */
player_bounce(player_t * player)1410 void player_bounce(player_t *player)
1411 {
1412 input_simulate_button_down(player->actor->input, IB_FIRE1);
1413 player->spring = FALSE;
1414 player->actor->speed.y = -player->actor->jump_strength;
1415 player->actor->is_jumping = TRUE;
1416 player->is_fire_jumping = FALSE;
1417 player->flying = FALSE;
1418 }
1419
1420
1421 /*
1422 * player_get_rings()
1423 * Returns the amount of rings
1424 * the player has got so far
1425 */
player_get_rings()1426 int player_get_rings()
1427 {
1428 return rings;
1429 }
1430
1431
1432
1433 /*
1434 * player_set_rings()
1435 * Sets a new amount of rings
1436 */
player_set_rings(int r)1437 void player_set_rings(int r)
1438 {
1439 rings = clip(r, 0, 9999);
1440
1441 /* (100+) * k rings (k integer) = new life! */
1442 if(r/100 > hundred_rings) {
1443 hundred_rings = r/100;
1444 player_set_lives( player_get_lives()+1 );
1445 level_override_music( soundfactory_get("1up") );
1446 }
1447 }
1448
1449
1450
1451 /*
1452 * player_get_lives()
1453 * How many lives does the player have?
1454 */
player_get_lives()1455 int player_get_lives()
1456 {
1457 return lives;
1458 }
1459
1460
1461
1462 /*
1463 * player_set_lives()
1464 * Sets the number of lives
1465 */
player_set_lives(int l)1466 void player_set_lives(int l)
1467 {
1468 lives = max(0, l);
1469 }
1470
1471
1472
1473 /*
1474 * player_get_score()
1475 * Returns the score
1476 */
player_get_score()1477 int player_get_score()
1478 {
1479 return score;
1480 }
1481
1482
1483
1484 /*
1485 * player_set_score()
1486 * Sets the score
1487 */
player_set_score(int s)1488 void player_set_score(int s)
1489 {
1490 score = max(0, s);
1491 }
1492
1493
1494
1495
1496
1497 /*
1498 * player_hit()
1499 * Hits a player. If it has no rings, then
1500 * it must die
1501 */
player_hit(player_t * player)1502 void player_hit(player_t *player)
1503 {
1504 actor_t *act = player->actor;
1505 item_t *ring;
1506 int i;
1507 int get_hit = FALSE;
1508
1509 if(!player->blinking && !player->dying && !player->invincible) {
1510 drop_glasses(player);
1511 if(player->shield_type != SH_NONE) {
1512 /* lose shield */
1513 get_hit = TRUE;
1514 player->shield_type = SH_NONE;
1515 sound_play( soundfactory_get("death") );
1516 }
1517 else if(rings > 0) {
1518 /* lose rings */
1519 get_hit = TRUE;
1520 for(i=0; i<min(player_get_rings(), 30); i++) {
1521 ring = level_create_item(IT_RING, act->position);
1522 ring_start_bouncing(ring);
1523 }
1524 player_set_rings(0);
1525 sound_play( soundfactory_get("ringless") );
1526 }
1527 else {
1528 /* death */
1529 player_kill(player);
1530 }
1531 }
1532
1533 if(get_hit) {
1534 player->getting_hit = TRUE;
1535 player->flying = player->landing = player->climbing = player->spring = FALSE;
1536 player->is_fire_jumping = FALSE;
1537 player->spin_dash = player->spin = FALSE;
1538 player->blinking = TRUE;
1539 player->blink_timer = 0;
1540 act->speed.x = act->mirror&IF_HFLIP ? 200 : -200;
1541 act->speed.y = -act->jump_strength*0.75;
1542 actor_move(act, v2d_new(0, -5));
1543 }
1544 }
1545
1546
1547
1548 /*
1549 * player_kill()
1550 * Kills a player
1551 */
player_kill(player_t * player)1552 void player_kill(player_t *player)
1553 {
1554 if(!player->dying) {
1555 drop_glasses(player);
1556 player->shield_type = SH_NONE;
1557 player->invincible = FALSE;
1558 player->got_speedshoes = FALSE;
1559 player->dying = TRUE;
1560 player->death_timer = 0;
1561 player->spring = FALSE;
1562 player->actor->speed.y = -player->actor->jump_strength*1.2;
1563 player->flying = player->climbing = player->landing = FALSE;
1564 player->is_fire_jumping = FALSE;
1565 player->spin = player->spin_dash = FALSE;
1566 player->blinking = FALSE;
1567 sound_play( soundfactory_get("death") );
1568 }
1569 }
1570
1571
1572
1573 /*
1574 * player_attacking()
1575 * Returns TRUE if a given player is attacking;
1576 * FALSE otherwise
1577 */
player_attacking(player_t * player)1578 int player_attacking(player_t *player)
1579 {
1580 animation_t *jump = sprite_get_animation(get_sprite_id(player->type), 3);
1581 return player->spin || player->spin_dash ||
1582 (/*player->actor->is_jumping &&*/ player->actor->animation == jump) ||
1583 (player->type == PL_KNUCKLES && (player->landing || player->flying));
1584 }
1585
1586
1587 /*
1588 * player_get_sprite_name()
1589 * Returns the name of the sprite used by the player
1590 */
player_get_sprite_name(player_t * player)1591 const char *player_get_sprite_name(player_t *player)
1592 {
1593 return get_sprite_id(player->type);
1594 }
1595
1596
1597
1598 /* private functions */
get_sprite_id(int player_type)1599 const char *get_sprite_id(int player_type)
1600 {
1601 switch(player_type) {
1602 case PL_SONIC:
1603 return "SD_SONIC";
1604
1605 case PL_TAILS:
1606 return "SD_TAILS";
1607
1608 case PL_KNUCKLES:
1609 return "SD_KNUCKLES";
1610
1611 default:
1612 return "null";
1613 }
1614 }
1615
1616
update_glasses(player_t * p)1617 void update_glasses(player_t *p)
1618 {
1619 int frame_id = 0, hflip = p->actor->mirror & IF_HFLIP;
1620 int visible = TRUE;
1621 float ang = old_school_angle(p->actor->angle);
1622 v2d_t gpos = v2d_new(0,0);
1623 v2d_t top = v2d_subtract(p->actor->position,v2d_rotate(v2d_new(0,p->actor->hot_spot.y),-ang));
1624 animation_t *anim = p->actor->animation;
1625
1626
1627
1628 switch(p->type) {
1629
1630
1631 case PL_SONIC:
1632 if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 0)) {
1633 /* stopped */
1634 gpos = v2d_new(3,24);
1635 frame_id = 1;
1636 }
1637 else if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 1)) {
1638 /* walking */
1639 switch((int)p->actor->animation_frame) {
1640 case 0: frame_id = 2; gpos = v2d_new(5,23); break;
1641 case 1: frame_id = 2; gpos = v2d_new(4,25); break;
1642 case 2: frame_id = 1; gpos = v2d_new(7,25); break;
1643 case 3: frame_id = 1; gpos = v2d_new(5,23); break;
1644 case 4: frame_id = 1; gpos = v2d_new(5,23); break;
1645 case 5: frame_id = 1; gpos = v2d_new(4,24); break;
1646 case 6: frame_id = 2; gpos = v2d_new(6,24); break;
1647 case 7: frame_id = 2; gpos = v2d_new(6,23); break;
1648 }
1649 }
1650 else if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 2)) {
1651 /* running */
1652 frame_id = 1;
1653 gpos = v2d_new(8,26);
1654 }
1655 else if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 5)) {
1656 /* look up */
1657 frame_id = 3;
1658 if((int)p->actor->animation_frame == 0)
1659 gpos = v2d_new(0,19);
1660 else
1661 gpos = v2d_new(-1,21);
1662 }
1663 else if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 7)) {
1664 /* braking */
1665 frame_id = 1;
1666 if((int)p->actor->animation_frame < 2)
1667 gpos = v2d_new(8,26);
1668 else
1669 gpos = v2d_new(10,28);
1670 }
1671 else if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 10)) {
1672 /* almost falling / ledge */
1673 frame_id = 1;
1674 switch((int)p->actor->animation_frame) {
1675 case 0: gpos = v2d_new(1,22); break;
1676 case 1: gpos = v2d_new(-1,23); break;
1677 case 2: gpos = v2d_new(1,23); break;
1678 }
1679 }
1680 else if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 11)) {
1681 /* ringless */
1682 frame_id = 3;
1683 gpos = v2d_new(-4,30);
1684 }
1685 else if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 12)) {
1686 /* breathing */
1687 frame_id = 3;
1688 gpos = v2d_new(1,19);
1689 }
1690 else if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 13)) {
1691 /* spring */
1692 frame_id = 3;
1693 gpos = v2d_new(4,13);
1694 }
1695 else if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 14)) {
1696 /* pushing */
1697 frame_id = 1;
1698 gpos = v2d_new(12,31);
1699 }
1700 else if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 15)) {
1701 /* waiting */
1702 frame_id = 0;
1703 gpos = v2d_new(3,23);
1704 }
1705 else if(anim == sprite_get_animation(get_sprite_id(PL_SONIC), 25)) {
1706 /* being carried */
1707 frame_id = 0;
1708 gpos = v2d_new(3,22);
1709 }
1710 else
1711 visible = FALSE;
1712 break;
1713
1714
1715
1716 case PL_TAILS:
1717 if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 0)) {
1718 /* stopped */
1719 gpos = v2d_new(5,34);
1720 frame_id = 1;
1721 }
1722 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 1)) {
1723 /* walking */
1724 frame_id = 2;
1725 switch((int)p->actor->animation_frame) {
1726 case 0: gpos = v2d_new(2,33); break;
1727 case 1: gpos = v2d_new(3,33); break;
1728 case 2: gpos = v2d_new(8,33); break;
1729 case 3: gpos = v2d_new(3,32); break;
1730 case 4: gpos = v2d_new(1,33); break;
1731 case 5: gpos = v2d_new(3,33); break;
1732 case 6: gpos = v2d_new(7,33); break;
1733 case 7: gpos = v2d_new(3,32); break;
1734 }
1735 }
1736 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 2)) {
1737 /* running */
1738 frame_id = 2;
1739 if((int)p->actor->animation_frame == 0)
1740 gpos = v2d_new(7,35);
1741 else
1742 gpos = v2d_new(6,34);
1743 }
1744 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 4)) {
1745 /* crouch down */
1746 frame_id = 1;
1747 gpos = v2d_new(9,44);
1748 }
1749 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 5)) {
1750 /* look up */
1751 frame_id = 1;
1752 gpos = v2d_new(7,32);
1753 }
1754 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 7)) {
1755 /* braking */
1756 frame_id = 1;
1757 if((int)p->actor->animation_frame == 0)
1758 gpos = v2d_new(2,33);
1759 else
1760 gpos = v2d_new(4,33);
1761 }
1762 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 10)) {
1763 /* almost falling / ledge */
1764 frame_id = 4;
1765 switch((int)p->actor->animation_frame) {
1766 case 0: gpos = v2d_new(5,33); break;
1767 case 1: gpos = v2d_new(6,33); break;
1768 }
1769 }
1770 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 11)) {
1771 /* ringless */
1772 frame_id = 1;
1773 gpos = v2d_new(1,33);
1774 }
1775 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 12)) {
1776 /* breathing */
1777 frame_id = 1;
1778 gpos = v2d_new(6,28);
1779 }
1780 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 13)) {
1781 /* spring */
1782 frame_id = 3;
1783 gpos = v2d_new(2,17);
1784 }
1785 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 14)) {
1786 /* pushing */
1787 frame_id = 1;
1788 gpos = v2d_new(9,35);
1789 }
1790 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 15)) {
1791 /* waiting */
1792 frame_id = 4;
1793 switch((int)p->actor->animation_frame) {
1794 case 0: case 8: case 9: case 10: gpos = v2d_new(5,34); break;
1795 default: gpos = v2d_new(5,33); break;
1796 }
1797 }
1798 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 16)) {
1799 /* carrying */
1800 frame_id = 1;
1801 gpos = v2d_new(8,37);
1802 }
1803 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 19)) {
1804 /* tired of flying */
1805 frame_id = 1;
1806 if((int)p->actor->animation_frame == 0)
1807 gpos = v2d_new(9,39);
1808 else
1809 gpos = v2d_new(9,40);
1810 }
1811 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 20)) {
1812 /* flying */
1813 frame_id = 1;
1814 gpos = v2d_new(8,39);
1815 }
1816 else if(anim == sprite_get_animation(get_sprite_id(PL_TAILS), 25)) {
1817 /* being carried */
1818 frame_id = 1;
1819 gpos = v2d_new(0,23);
1820 }
1821 else
1822 visible = FALSE;
1823 break;
1824
1825
1826 case PL_KNUCKLES:
1827 if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 0)) {
1828 /* stopped */
1829 frame_id = 1;
1830 gpos = v2d_new(1,24);
1831 }
1832 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 1)) {
1833 /* walking */
1834 switch((int)p->actor->animation_frame) {
1835 case 0: frame_id = 1; gpos = v2d_new(5,29); break;
1836 case 1: frame_id = 2; gpos = v2d_new(5,29); break;
1837 case 2: frame_id = 2; gpos = v2d_new(8,29); break;
1838 case 3: frame_id = 2; gpos = v2d_new(9,28); break;
1839 case 4: frame_id = 1; gpos = v2d_new(6,28); break;
1840 case 5: frame_id = 1; gpos = v2d_new(6,29); break;
1841 case 6: frame_id = 1; gpos = v2d_new(5,28); break;
1842 case 7: frame_id = 1; gpos = v2d_new(4,27); break;
1843 }
1844 }
1845 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 2)) {
1846 /* running */
1847 frame_id = 1;
1848 gpos = v2d_new(7,29);
1849 }
1850 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 4)) {
1851 /* crouch down */
1852 frame_id = 1;
1853 if((int)p->actor->animation_frame == 0)
1854 gpos = v2d_new(0,31);
1855 else
1856 gpos = v2d_new(0,40);
1857 }
1858 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 5)) {
1859 /* look up */
1860 frame_id = 1;
1861 if((int)p->actor->animation_frame == 0)
1862 gpos = v2d_new(0,21);
1863 else
1864 gpos = v2d_new(-1,21);
1865 }
1866 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 7)) {
1867 /* braking */
1868 frame_id = 0;
1869 gpos = v2d_new(-2,27);
1870 }
1871 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 10)) {
1872 /* almost falling / ledge */
1873 frame_id = 1;
1874 switch((int)p->actor->animation_frame) {
1875 case 0: gpos = v2d_new(9,30); break;
1876 case 1: gpos = v2d_new(8,27); break;
1877 }
1878 }
1879 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 11)) {
1880 /* ringless */
1881 frame_id = 1;
1882 gpos = v2d_new(-3,27);
1883 }
1884 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 12)) {
1885 /* breathing */
1886 frame_id = 1;
1887 gpos = v2d_new(5,24);
1888 }
1889 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 13)) {
1890 /* spring */
1891 frame_id = 3;
1892 gpos = v2d_new(-1,16);
1893 }
1894 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 14)) {
1895 /* pushing */
1896 switch((int)p->actor->animation_frame) {
1897 case 0: frame_id = 1; gpos = v2d_new(5,29); break;
1898 case 1: frame_id = 2; gpos = v2d_new(5,29); break;
1899 case 2: frame_id = 2; gpos = v2d_new(8,29); break;
1900 case 3: frame_id = 2; gpos = v2d_new(9,28); break;
1901 case 4: frame_id = 1; gpos = v2d_new(6,28); break;
1902 case 5: frame_id = 1; gpos = v2d_new(6,29); break;
1903 case 6: frame_id = 1; gpos = v2d_new(5,28); break;
1904 case 7: frame_id = 1; gpos = v2d_new(4,27); break;
1905 }
1906 }
1907 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 15)) {
1908 /* waiting */
1909 frame_id = 0;
1910 gpos = v2d_new(1,23);
1911 }
1912 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 16)) {
1913 /* no more climbing */
1914 frame_id = 1;
1915 switch((int)p->actor->animation_frame) {
1916 case 0: gpos = v2d_new(6,23); break;
1917 case 1: gpos = v2d_new(5,20); break;
1918 case 2: gpos = v2d_new(0,22); break;
1919 }
1920 }
1921 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 17)) {
1922 /* climbing */
1923 frame_id = 3;
1924 switch((int)p->actor->animation_frame) {
1925 case 0: gpos = v2d_new(-1,22); break;
1926 case 1: gpos = v2d_new(-2,20); break;
1927 case 2: gpos = v2d_new(0,21); break;
1928 case 3: gpos = v2d_new(-1,24); break;
1929 case 4: gpos = v2d_new(0,23); break;
1930 case 5: gpos = v2d_new(0,22); break;
1931 }
1932 }
1933 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 18)) {
1934 /* end of flight */
1935 frame_id = 1;
1936 if((int)p->actor->animation_frame == 0)
1937 gpos = v2d_new(6,23);
1938 else
1939 gpos = v2d_new(5,20);
1940 }
1941 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 19)) {
1942 /* flying - ground */
1943 frame_id = 1;
1944 gpos = v2d_new(8,44);
1945 }
1946 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 20)) {
1947 /* flying - air */
1948 frame_id = 1;
1949 gpos = v2d_new(8,39);
1950 }
1951 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 21)) {
1952 /* flying - turn */
1953 frame_id = 4;
1954 switch((int)p->actor->animation_frame) {
1955 case 0: gpos = v2d_new(-8,41); break;
1956 case 1: gpos = v2d_new(0,43); break;
1957 case 2: gpos = v2d_new(10,41); break;
1958 }
1959 }
1960 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 22)) {
1961 /* climbing - stopped */
1962 frame_id = 3;
1963 gpos = v2d_new(0,22);
1964 }
1965 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 23)) {
1966 /* climbing - reached the top */
1967 switch((int)p->actor->animation_frame) {
1968 case 0: frame_id = 3; gpos = v2d_new(7,17); break;
1969 case 1: frame_id = 3; gpos = v2d_new(11,15); break;
1970 case 2: frame_id = 0; gpos = v2d_new(12,13); break;
1971 }
1972 }
1973 else if(anim == sprite_get_animation(get_sprite_id(PL_KNUCKLES), 25)) {
1974 /* being carried */
1975 frame_id = 0;
1976 gpos = v2d_new(0,23);
1977 }
1978 else
1979 visible = FALSE;
1980 break;
1981 }
1982
1983 gpos.x *= hflip ? -1 : 1;
1984 actor_change_animation(p->glasses, sprite_get_animation("SD_GLASSES", frame_id));
1985 p->glasses->position = v2d_add(top, v2d_rotate(gpos, -ang));
1986 p->glasses->angle = ang;
1987 p->glasses->mirror = p->actor->mirror;
1988 p->glasses->visible = visible && p->actor->visible;
1989 }
1990
1991
1992
drop_glasses(player_t * p)1993 void drop_glasses(player_t *p)
1994 {
1995 if(p->got_glasses) {
1996 v2d_t pos = v2d_add(p->actor->position, v2d_new(0,-27));
1997 item_t *item = level_create_item(IT_FALGLASSES, pos);
1998 falglasses_set_speed(item, v2d_new(-0.2f * p->actor->speed.x, -490.0f));
1999 p->got_glasses = FALSE;
2000 }
2001 }
2002
2003
2004
update_shield(player_t * p)2005 void update_shield(player_t *p)
2006 {
2007 actor_t *sh = p->shield, *act = p->actor;
2008 v2d_t off = v2d_new(0,0);
2009
2010 switch(p->shield_type) {
2011
2012 case SH_SHIELD:
2013 off = v2d_new(0,-22);
2014 sh->position = v2d_add(act->position, v2d_rotate(off, -old_school_angle(act->angle)));
2015 actor_change_animation(sh, sprite_get_animation("SD_SHIELD", 0));
2016 break;
2017
2018 case SH_FIRESHIELD:
2019 off = v2d_new(0,-22);
2020 sh->position = v2d_add(act->position, v2d_rotate(off, -old_school_angle(act->angle)));
2021 actor_change_animation(sh, sprite_get_animation("SD_FIRESHIELD", 0));
2022 break;
2023
2024 case SH_THUNDERSHIELD:
2025 off = v2d_new(0,-22);
2026 sh->position = v2d_add(act->position, v2d_rotate(off, -old_school_angle(act->angle)));
2027 actor_change_animation(sh, sprite_get_animation("SD_THUNDERSHIELD", 0));
2028 break;
2029
2030 case SH_WATERSHIELD:
2031 off = v2d_new(0,-22);
2032 sh->position = v2d_add(act->position, v2d_rotate(off, -old_school_angle(act->angle)));
2033 actor_change_animation(sh, sprite_get_animation("SD_WATERSHIELD", 0));
2034 break;
2035
2036 case SH_ACIDSHIELD:
2037 off = v2d_new(0,-22);
2038 sh->position = v2d_add(act->position, v2d_rotate(off, -old_school_angle(act->angle)));
2039 actor_change_animation(sh, sprite_get_animation("SD_ACIDSHIELD", 0));
2040 break;
2041
2042 case SH_WINDSHIELD:
2043 off = v2d_new(0,-22);
2044 sh->position = v2d_add(act->position, v2d_rotate(off, -old_school_angle(act->angle)));
2045 actor_change_animation(sh, sprite_get_animation("SD_WINDSHIELD", 0));
2046 break;
2047 }
2048 }
2049
2050
2051 /* is the player inside a loop? */
inside_loop(player_t * p)2052 int inside_loop(player_t *p)
2053 {
2054 return (p->disable_wall != PLAYER_WALL_NONE);
2055 }
2056
2057 /* the player won't leave the floor unless necessary */
stickyphysics_hack(player_t * player,brick_list_t * brick_list,brick_t ** brick_downleft,brick_t ** brick_down,brick_t ** brick_downright)2058 void stickyphysics_hack(player_t *player, brick_list_t *brick_list, brick_t **brick_downleft, brick_t **brick_down, brick_t **brick_downright)
2059 {
2060 actor_t *act = player->actor;
2061 float oldy = act->position.y;
2062
2063 if(NULL == *brick_down && !act->is_jumping && !player->is_fire_jumping && !player->flying && !player->climbing && !player->landing && !player->spring && !player->getting_hit && !player->dead && !player->dying) {
2064 int i;
2065 float sqrsize=2, diff=-2;
2066 brick_t *downleft, *down, *downright;
2067 for(i=0; i<8; i++) {
2068 act->position.y = oldy + (float)(1+i);
2069 actor_corners(act, sqrsize, diff, brick_list, NULL, NULL, NULL, &downright, &down, &downleft, NULL, NULL);
2070 if(NULL != down) {
2071 *brick_downleft = downleft;
2072 *brick_down = down;
2073 *brick_downright = downright;
2074 return;
2075 }
2076 }
2077 }
2078
2079 act->position.y = oldy;
2080 }
2081
2082 /* aaaargh!! the player is being crushed! */
got_crushed(player_t * p,brick_t * brick_up,brick_t * brick_right,brick_t * brick_down,brick_t * brick_left)2083 int got_crushed(player_t *p, brick_t *brick_up, brick_t *brick_right, brick_t *brick_down, brick_t *brick_left)
2084 {
2085 float sx, sy, t;
2086
2087 if(p->climbing)
2088 return FALSE;
2089
2090 /* y-axis */
2091 if(brick_up && brick_down && brick_up != brick_down) {
2092 if(brick_up->brick_ref->behavior == BRB_CIRCULAR && brick_up->brick_ref->property == BRK_OBSTACLE) {
2093 t = brick_up->value[0];
2094 sy = brick_up->brick_ref->behavior_arg[3];
2095 if(sin(sy * t) > 0) return TRUE; /* crushed! */
2096 }
2097
2098 if(brick_down->brick_ref->behavior == BRB_CIRCULAR && brick_down->brick_ref->property == BRK_OBSTACLE) {
2099 t = brick_down->value[0];
2100 sy = brick_down->brick_ref->behavior_arg[3];
2101 if(sin(sy * t) < 0) return TRUE; /* crushed! */
2102 }
2103 }
2104
2105 /* x-axis */
2106 if(brick_left && brick_right && brick_left != brick_right) {
2107 if(brick_left->brick_ref->behavior == BRB_CIRCULAR && brick_left->brick_ref->property == BRK_OBSTACLE) {
2108 t = brick_left->value[0];
2109 sx = brick_left->brick_ref->behavior_arg[2];
2110 if(cos(sx * t) > 0) return TRUE; /* crushed! */
2111 }
2112
2113 if(brick_right->brick_ref->behavior == BRB_CIRCULAR && brick_right->brick_ref->property == BRK_OBSTACLE) {
2114 t = brick_right->value[0];
2115 sx = brick_right->brick_ref->behavior_arg[2];
2116 if(cos(sx * t) < 0) return TRUE; /* crushed! */
2117 }
2118 }
2119
2120 /* I'm not being crushed */
2121 return FALSE;
2122 }
2123