1 /*
2  * actor.c - actor 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 <allegro.h>
21 #include <math.h>
22 #include "actor.h"
23 #include "../core/global.h"
24 #include "../core/input.h"
25 #include "../scenes/level.h"
26 #include "../core/util.h"
27 #include "../core/logfile.h"
28 #include "../core/video.h"
29 #include "../core/timer.h"
30 
31 
32 /* constants */
33 #define MAGIC_DIFF              -2  /* platform movement & collision detectors magic */
34 #define SIDE_CORNERS_HEIGHT     0.5 /* height of the left/right sensors */
35 
36 
37 /* private data */
38 static int floor_priority = TRUE; /* default behavior: priority(floor) > priority(wall) */
39 static int slope_priority = TRUE; /* default behavior: priority(slope) > priority(floor) */
40 static brick_t* brick_at(brick_list_t *list, float rect[4]);
41 static int is_leftwall_disabled = FALSE;
42 static int is_rightwall_disabled = FALSE;
43 static int is_floor_disabled = FALSE;
44 static int is_ceiling_disabled = FALSE;
45 
46 /* private functions */
47 static void calculate_rotated_boundingbox(const actor_t *act, v2d_t spot[4]);
48 
49 
50 /* actor functions */
51 
52 
53 
54 /*
55  * actor_create()
56  * Creates an actor
57  */
actor_create()58 actor_t* actor_create()
59 {
60     actor_t *act = mallocx(sizeof *act);
61 
62     act->spawn_point = v2d_new(0,0);
63     act->position = act->spawn_point;
64     act->angle = 0.0f;
65     act->speed = v2d_new(0,0);
66     act->maxspeed = 0.0f;
67     act->acceleration = 0.0f;
68     act->jump_strength = 0.0f;
69     act->is_jumping = FALSE;
70     act->ignore_horizontal = FALSE;
71     act->input = NULL;
72 
73     act->animation = NULL;
74     act->animation_frame = 0.0f;
75     act->animation_speed_factor = 1.0f;
76     act->mirror = IF_NONE;
77     act->visible = TRUE;
78     act->alpha = 1.0f;
79     act->hot_spot = v2d_new(0,0);
80 
81     act->carried_by = NULL;
82     act->carry_offset = v2d_new(0,0);
83     act->carrying = NULL;
84 
85     return act;
86 }
87 
88 
89 /*
90  * actor_destroy()
91  * Destroys an actor
92  */
actor_destroy(actor_t * act)93 void actor_destroy(actor_t *act)
94 {
95     if(act->input)
96         input_destroy(act->input);
97     free(act);
98 }
99 
100 
101 /*
102  * actor_render()
103  * Default rendering function
104  */
actor_render(actor_t * act,v2d_t camera_position)105 void actor_render(actor_t *act, v2d_t camera_position)
106 {
107     float diff = MAGIC_DIFF;
108     image_t *img;
109     v2d_t tmp;
110 
111     if(act->visible && act->animation) {
112         /* update animation */
113         act->animation_frame += (act->animation->fps * act->animation_speed_factor) * timer_get_delta();
114         if((int)act->animation_frame >= act->animation->frame_count) {
115             if(act->animation->repeat)
116                 act->animation_frame = (int)act->animation_frame % act->animation->frame_count;
117             else
118                 act->animation_frame = act->animation->frame_count-1;
119         }
120 
121         /* render */
122         tmp = act->position;
123         img = actor_image(act);
124         actor_move(act, v2d_new(0,-diff));
125         if(fabs(act->angle) > EPSILON)
126            image_draw_rotated(img, video_get_backbuffer(), (int)(act->position.x-(camera_position.x-VIDEO_SCREEN_W/2)), (int)(act->position.y-(camera_position.y-VIDEO_SCREEN_H/2)), (int)act->hot_spot.x, (int)act->hot_spot.y, act->angle, act->mirror);
127         else if(fabs(act->alpha - 1.0f) > EPSILON)
128            image_draw_trans(img, video_get_backbuffer(), (int)(act->position.x-act->hot_spot.x-(camera_position.x-VIDEO_SCREEN_W/2)), (int)(act->position.y-act->hot_spot.y-(camera_position.y-VIDEO_SCREEN_H/2)), image_rgb(255,255,255), act->alpha, act->mirror);
129         else
130            image_draw(img, video_get_backbuffer(), (int)(act->position.x-act->hot_spot.x-(camera_position.x-VIDEO_SCREEN_W/2)), (int)(act->position.y-act->hot_spot.y-(camera_position.y-VIDEO_SCREEN_H/2)), act->mirror);
131         act->position = tmp;
132     }
133 }
134 
135 
136 
137 /*
138  * actor_render_repeat_xy()
139  * Rendering / repeat xy
140  */
actor_render_repeat_xy(actor_t * act,v2d_t camera_position,int repeat_x,int repeat_y)141 void actor_render_repeat_xy(actor_t *act, v2d_t camera_position, int repeat_x, int repeat_y)
142 {
143     int i, j, w, h;
144     image_t *img = actor_image(act);
145     v2d_t final_pos;
146 
147     final_pos.x = (int)act->position.x%(repeat_x?img->w:INT_MAX) - act->hot_spot.x-(camera_position.x-VIDEO_SCREEN_W/2) - (repeat_x?img->w:0);
148     final_pos.y = (int)act->position.y%(repeat_y?img->h:INT_MAX) - act->hot_spot.y-(camera_position.y-VIDEO_SCREEN_H/2) - (repeat_y?img->h:0);
149 
150     if(act->visible && act->animation) {
151         /* update animation */
152         act->animation_frame += (act->animation->fps * act->animation_speed_factor) * timer_get_delta();
153         if((int)act->animation_frame >= act->animation->frame_count) {
154             if(act->animation->repeat)
155                 act->animation_frame = (int)act->animation_frame % act->animation->frame_count;
156             else
157                 act->animation_frame = act->animation->frame_count-1;
158         }
159 
160         /* render */
161         w = repeat_x ? (VIDEO_SCREEN_W/img->w + 3) : 1;
162         h = repeat_y ? (VIDEO_SCREEN_H/img->h + 3) : 1;
163         for(i=0; i<w; i++) {
164             for(j=0; j<h; j++)
165                 image_draw(img, video_get_backbuffer(), (int)final_pos.x + i*img->w, (int)final_pos.y + j*img->h, act->mirror);
166         }
167     }
168 }
169 
170 
171 
172 /*
173  * actor_render_corners()
174  * Renders the corners (sensors) of the actor.
175  *
176  * Let X be the original collision detector spot. So:
177  * diff = distance between the real collision detector spot and X
178  * sqrsize = size of the collision detector
179  */
actor_render_corners(const actor_t * act,float sqrsize,float diff,v2d_t camera_position)180 void actor_render_corners(const actor_t *act, float sqrsize, float diff, v2d_t camera_position)
181 {
182     uint32 c[2];
183     v2d_t offset = v2d_subtract(camera_position, v2d_new(VIDEO_SCREEN_W/2, VIDEO_SCREEN_H/2));
184     int frame_width = actor_image(act)->w;
185     int frame_height = actor_image(act)->h;
186 
187     v2d_t feet      = v2d_subtract(act->position, offset);
188     v2d_t vup        = v2d_add ( feet , v2d_rotate( v2d_new(0, -frame_height+diff), -act->angle) );
189     v2d_t vdown      = v2d_add ( feet , v2d_rotate( v2d_new(0, -diff), -act->angle) );
190     v2d_t vleft      = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width/2+diff, -frame_height*SIDE_CORNERS_HEIGHT), -act->angle) );
191     v2d_t vright     = v2d_add ( feet , v2d_rotate( v2d_new(frame_width/2-diff, -frame_height*SIDE_CORNERS_HEIGHT), -act->angle) );
192     v2d_t vupleft    = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width/2+diff, -frame_height+diff), -act->angle) );
193     v2d_t vupright   = v2d_add ( feet , v2d_rotate( v2d_new(frame_width/2-diff, -frame_height+diff), -act->angle) );
194     v2d_t vdownleft  = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width/2+diff, -diff), -act->angle) );
195     v2d_t vdownright = v2d_add ( feet , v2d_rotate( v2d_new(frame_width/2-diff, -diff), -act->angle) );
196 
197     float cd_up[4] = { vup.x-sqrsize , vup.y-sqrsize , vup.x+sqrsize , vup.y+sqrsize };
198     float cd_down[4] = { vdown.x-sqrsize , vdown.y-sqrsize , vdown.x+sqrsize , vdown.y+sqrsize };
199     float cd_left[4] = { vleft.x-sqrsize , vleft.y-sqrsize , vleft.x+sqrsize , vleft.y+sqrsize };
200     float cd_right[4] = { vright.x-sqrsize , vright.y-sqrsize , vright.x+sqrsize , vright.y+sqrsize };
201     float cd_upleft[4] = { vupleft.x-sqrsize , vupleft.y-sqrsize , vupleft.x+sqrsize , vupleft.y+sqrsize };
202     float cd_upright[4] = { vupright.x-sqrsize , vupright.y-sqrsize , vupright.x+sqrsize , vupright.y+sqrsize };
203     float cd_downleft[4] = { vdownleft.x-sqrsize , vdownleft.y-sqrsize , vdownleft.x+sqrsize , vdownleft.y+sqrsize };
204     float cd_downright[4] = { vdownright.x-sqrsize , vdownright.y-sqrsize , vdownright.x+sqrsize , vdownright.y+sqrsize };
205 
206     c[0] = image_rgb(255,255,255);
207     c[1] = image_rgb(0,128,255);
208 
209     image_rectfill(video_get_backbuffer(), cd_up[0], cd_up[1], cd_up[2], cd_up[3], c[0]);
210     image_rectfill(video_get_backbuffer(), cd_down[0], cd_down[1], cd_down[2], cd_down[3], c[0]);
211     image_rectfill(video_get_backbuffer(), cd_left[0], cd_left[1], cd_left[2], cd_left[3], c[0]);
212     image_rectfill(video_get_backbuffer(), cd_right[0], cd_right[1], cd_right[2], cd_right[3], c[0]);
213     image_rectfill(video_get_backbuffer(), cd_downleft[0], cd_downleft[1], cd_downleft[2], cd_downleft[3], c[1]);
214     image_rectfill(video_get_backbuffer(), cd_downright[0], cd_downright[1], cd_downright[2], cd_downright[3], c[1]);
215     image_rectfill(video_get_backbuffer(), cd_upright[0], cd_upright[1], cd_upright[2], cd_upright[3], c[1]);
216     image_rectfill(video_get_backbuffer(), cd_upleft[0], cd_upleft[1], cd_upleft[2], cd_upleft[3], c[1]);
217 }
218 
219 
220 /*
221  * actor_collision()
222  * Check collisions
223  */
actor_collision(const actor_t * a,const actor_t * b)224 int actor_collision(const actor_t *a, const actor_t *b)
225 {
226     int j, right = 0;
227     v2d_t corner[2][4];
228     corner[0][0] = v2d_subtract(a->position, v2d_rotate(a->hot_spot, -a->angle)); /* a's topleft */
229     corner[0][1] = v2d_add( corner[0][0] , v2d_rotate(v2d_new(actor_image(a)->w, 0), -a->angle) ); /* a's topright */
230     corner[0][2] = v2d_add( corner[0][0] , v2d_rotate(v2d_new(actor_image(a)->w, actor_image(a)->h), -a->angle) ); /* a's bottomright */
231     corner[0][3] = v2d_add( corner[0][0] , v2d_rotate(v2d_new(0, actor_image(a)->h), -a->angle) ); /* a's bottomleft */
232     corner[1][0] = v2d_subtract(b->position, v2d_rotate(b->hot_spot, -b->angle)); /* b's topleft */
233     corner[1][1] = v2d_add( corner[1][0] , v2d_rotate(v2d_new(actor_image(b)->w, 0), -b->angle) ); /* b's topright */
234     corner[1][2] = v2d_add( corner[1][0] , v2d_rotate(v2d_new(actor_image(b)->w, actor_image(b)->h), -b->angle) ); /* b's bottomright */
235     corner[1][3] = v2d_add( corner[1][0] , v2d_rotate(v2d_new(0, actor_image(b)->h), -b->angle) ); /* b's bottomleft */
236     right += fabs(a->angle)<EPSILON||fabs(a->angle-PI/2)<EPSILON||fabs(a->angle-PI)<EPSILON||fabs(a->angle-3*PI/2)<EPSILON;
237     right += fabs(b->angle)<EPSILON||fabs(b->angle-PI/2)<EPSILON||fabs(b->angle-PI)<EPSILON||fabs(b->angle-3*PI/2)<EPSILON;
238 
239     if(right) {
240         float r[2][4];
241         for(j=0; j<2; j++) {
242             r[j][0] = min(corner[j][0].x, corner[j][1].x);
243             r[j][1] = min(corner[j][0].y, corner[j][1].y);
244             r[j][2] = max(corner[j][2].x, corner[j][3].x);
245             r[j][3] = max(corner[j][2].y, corner[j][3].y);
246             if(r[j][0] > r[j][2]) swap(r[j][0], r[j][2]);
247             if(r[j][1] > r[j][3]) swap(r[j][1], r[j][3]);
248         }
249         return bounding_box(r[0],r[1]);
250     }
251     else {
252         v2d_t center[2];
253         float radius[2] = { max(actor_image(a)->w,actor_image(a)->h) , max(actor_image(b)->w,actor_image(b)->h) };
254         for(j=0; j<2; j++)
255             center[j] = v2d_multiply(v2d_add(corner[j][0], corner[j][2]), 0.5);
256         return circular_collision(center[0], radius[0], center[1], radius[1]);
257     }
258 }
259 
260 
261 /*
262  * actor_orientedbox_collision()
263  * Is a colliding with b? (oriented bounding box detection)
264  */
actor_orientedbox_collision(const actor_t * a,const actor_t * b)265 int actor_orientedbox_collision(const actor_t *a, const actor_t *b)
266 {
267     v2d_t a_pos, b_pos;
268     v2d_t a_size, b_size;
269     v2d_t a_spot[4], b_spot[4]; /* rotated spots */
270 
271     calculate_rotated_boundingbox(a, a_spot);
272     calculate_rotated_boundingbox(b, b_spot);
273 
274     a_pos.x = min(a_spot[0].x, min(a_spot[1].x, min(a_spot[2].x, a_spot[3].x)));
275     a_pos.y = min(a_spot[0].y, min(a_spot[1].y, min(a_spot[2].y, a_spot[3].y)));
276     b_pos.x = min(b_spot[0].x, min(b_spot[1].x, min(b_spot[2].x, b_spot[3].x)));
277     b_pos.y = min(b_spot[0].y, min(b_spot[1].y, min(b_spot[2].y, b_spot[3].y)));
278 
279     a_size.x = max(a_spot[0].x, max(a_spot[1].x, max(a_spot[2].x, a_spot[3].x))) - a_pos.x;
280     a_size.y = max(a_spot[0].y, max(a_spot[1].y, max(a_spot[2].y, a_spot[3].y))) - a_pos.y;
281     b_size.x = max(b_spot[0].x, max(b_spot[1].x, max(b_spot[2].x, b_spot[3].x))) - b_pos.x;
282     b_size.y = max(b_spot[0].y, max(b_spot[1].y, max(b_spot[2].y, b_spot[3].y))) - b_pos.y;
283 
284     if(a_pos.x + a_size.x >= b_pos.x && a_pos.x <= b_pos.x + b_size.x) {
285         if(a_pos.y + a_size.y >= b_pos.y && a_pos.y <= b_pos.y + b_size.y)
286             return TRUE;
287     }
288 
289     return FALSE;
290 }
291 
292 
293 /*
294  * actor_pixelperfect_collision()
295  * Is a colliding with b? (pixel perfect detection)
296  */
actor_pixelperfect_collision(const actor_t * a,const actor_t * b)297 int actor_pixelperfect_collision(const actor_t *a, const actor_t *b)
298 {
299 
300     if(fabs(a->angle) < EPSILON && fabs(b->angle) < EPSILON) {
301         if(actor_collision(a, b)) {
302             int x1, y1, x2, y2;
303 
304             x1 = (int)(a->position.x - a->hot_spot.x);
305             y1 = (int)(a->position.y - a->hot_spot.y);
306             x2 = (int)(b->position.x - b->hot_spot.x);
307             y2 = (int)(b->position.y - b->hot_spot.y);
308 
309             return image_pixelperfect_collision(actor_image(a), actor_image(b), x1, y1, x2, y2);
310         }
311         else
312             return FALSE;
313     }
314     else {
315         if(actor_orientedbox_collision(a, b)) {
316             image_t *image_a, *image_b;
317             v2d_t size_a, size_b, pos_a, pos_b;
318             v2d_t a_spot[4], b_spot[4]; /* rotated spots */
319             v2d_t ac, bc; /* rotation spot */
320             int collided;
321 
322             calculate_rotated_boundingbox(a, a_spot);
323             calculate_rotated_boundingbox(b, b_spot);
324 
325             pos_a.x = min(a_spot[0].x, min(a_spot[1].x, min(a_spot[2].x, a_spot[3].x)));
326             pos_a.y = min(a_spot[0].y, min(a_spot[1].y, min(a_spot[2].y, a_spot[3].y)));
327             pos_b.x = min(b_spot[0].x, min(b_spot[1].x, min(b_spot[2].x, b_spot[3].x)));
328             pos_b.y = min(b_spot[0].y, min(b_spot[1].y, min(b_spot[2].y, b_spot[3].y)));
329 
330             size_a.x = max(a_spot[0].x, max(a_spot[1].x, max(a_spot[2].x, a_spot[3].x))) - pos_a.x;
331             size_a.y = max(a_spot[0].y, max(a_spot[1].y, max(a_spot[2].y, a_spot[3].y))) - pos_a.y;
332             size_b.x = max(b_spot[0].x, max(b_spot[1].x, max(b_spot[2].x, b_spot[3].x))) - pos_b.x;
333             size_b.y = max(b_spot[0].y, max(b_spot[1].y, max(b_spot[2].y, b_spot[3].y))) - pos_b.y;
334 
335             ac = v2d_add(v2d_subtract(a_spot[0], pos_a), v2d_rotate(a->hot_spot, -a->angle));
336             bc = v2d_add(v2d_subtract(b_spot[0], pos_b), v2d_rotate(b->hot_spot, -b->angle));
337 
338             image_a = image_create(size_a.x, size_a.y);
339             image_b = image_create(size_b.x, size_b.y);
340             image_clear(image_a, video_get_maskcolor());
341             image_clear(image_b, video_get_maskcolor());
342 
343             image_draw_rotated(actor_image(a), image_a, ac.x, ac.y, (int)a->hot_spot.x, (int)a->hot_spot.y, a->angle, a->mirror);
344             image_draw_rotated(actor_image(b), image_b, bc.x, bc.y, (int)b->hot_spot.x, (int)b->hot_spot.y, b->angle, b->mirror);
345 
346             collided = image_pixelperfect_collision(image_a, image_b, pos_a.x, pos_a.y, pos_b.x, pos_b.y);
347 
348             image_destroy(image_a);
349             image_destroy(image_b);
350             return collided;
351         }
352         else
353             return FALSE;
354     }
355 }
356 
357 
358 /*
359  * actor_brick_collision()
360  * Actor collided with a brick?
361  */
actor_brick_collision(actor_t * act,brick_t * brk)362 int actor_brick_collision(actor_t *act, brick_t *brk)
363 {
364     v2d_t topleft = v2d_subtract(act->position, v2d_rotate(act->hot_spot, act->angle));
365     v2d_t bottomright = v2d_add( topleft , v2d_rotate(v2d_new(actor_image(act)->w, actor_image(act)->h), act->angle) );
366     float a[4] = { topleft.x , topleft.y , bottomright.x , bottomright.y };
367     float b[4] = { (float)brk->x , (float)brk->y , (float)(brk->x+brk->brick_ref->image->w) , (float)(brk->y+brk->brick_ref->image->h) };
368 
369     return bounding_box(a,b);
370 }
371 
372 
373 
374 /*
375  * actor_move()
376  * Uses the orientation angle to move an actor
377  */
actor_move(actor_t * act,v2d_t delta_space)378 void actor_move(actor_t *act, v2d_t delta_space)
379 {
380     if(fabs(delta_space.x) < EPSILON) delta_space.x = 0;
381     act->position.x += delta_space.x * cos(act->angle) + delta_space.y * sin(act->angle);
382     act->position.y += delta_space.y * cos(act->angle) - delta_space.x * sin(act->angle);
383 }
384 
385 
386 /*
387  * actor_change_animation()
388  * Changes the animation of an actor
389  */
actor_change_animation(actor_t * act,animation_t * anim)390 void actor_change_animation(actor_t *act, animation_t *anim)
391 {
392     if(act->animation != anim) {
393         act->animation = anim;
394         act->hot_spot = anim->hot_spot;
395         act->animation_frame = 0;
396         act->animation_speed_factor = 1.0;
397     }
398 }
399 
400 
401 
402 /*
403  * actor_change_animation_frame()
404  * Changes the animation frame
405  */
actor_change_animation_frame(actor_t * act,int frame)406 void actor_change_animation_frame(actor_t *act, int frame)
407 {
408     act->animation_frame = clip(frame, 0, act->animation->frame_count);
409 }
410 
411 
412 
413 /*
414  * actor_change_animation_speed_factor()
415  * Changes the speed factor of the current animation
416  * The default factor is 1.0 (i.e., 100% of the original
417  * animation speed)
418  */
actor_change_animation_speed_factor(actor_t * act,float factor)419 void actor_change_animation_speed_factor(actor_t *act, float factor)
420 {
421     act->animation_speed_factor = max(0.0, factor);
422 }
423 
424 
425 
426 /*
427  * actor_animation_finished()
428  * Returns true if the current animation has finished
429  */
actor_animation_finished(actor_t * act)430 int actor_animation_finished(actor_t *act)
431 {
432     float frame = act->animation_frame + (act->animation->fps * act->animation_speed_factor) * timer_get_delta();
433     return (!act->animation->repeat && (int)frame >= act->animation->frame_count);
434 }
435 
436 
437 
438 /*
439  * actor_image()
440  * Returns the current image of the
441  * animation of this actor
442  */
actor_image(const actor_t * act)443 image_t* actor_image(const actor_t *act)
444 {
445     return sprite_get_image(act->animation, (int)act->animation_frame);
446 }
447 
448 
449 
450 
451 
452 
453 /*
454  * actor_corners()
455  * Get actor's corners
456  *
457  * Let X be the original collision detector spot. So:
458  * diff = distance between the real collision detector spot and X
459  * sqrsize = size of the collision detector
460  */
actor_corners(actor_t * act,float sqrsize,float diff,brick_list_t * brick_list,brick_t ** up,brick_t ** upright,brick_t ** right,brick_t ** downright,brick_t ** down,brick_t ** downleft,brick_t ** left,brick_t ** upleft)461 void actor_corners(actor_t *act, float sqrsize, float diff, brick_list_t *brick_list, brick_t **up, brick_t **upright, brick_t **right, brick_t **downright, brick_t **down, brick_t **downleft, brick_t **left, brick_t **upleft)
462 {
463     int frame_width = actor_image(act)->w;
464     int frame_height = actor_image(act)->h;
465 
466     v2d_t feet      = act->position;
467     v2d_t vup        = v2d_add ( feet , v2d_rotate( v2d_new(0, -frame_height+diff), -act->angle) );
468     v2d_t vdown      = v2d_add ( feet , v2d_rotate( v2d_new(0, -diff), -act->angle) );
469     v2d_t vleft      = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width/2+diff, -frame_height*SIDE_CORNERS_HEIGHT), -act->angle) );
470     v2d_t vright     = v2d_add ( feet , v2d_rotate( v2d_new(frame_width/2-diff, -frame_height*SIDE_CORNERS_HEIGHT), -act->angle) );
471     v2d_t vupleft    = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width/2+diff, -frame_height+diff), -act->angle) );
472     v2d_t vupright   = v2d_add ( feet , v2d_rotate( v2d_new(frame_width/2-diff, -frame_height+diff), -act->angle) );
473     v2d_t vdownleft  = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width/2+diff, -diff), -act->angle) );
474     v2d_t vdownright = v2d_add ( feet , v2d_rotate( v2d_new(frame_width/2-diff, -diff), -act->angle) );
475 
476     actor_corners_ex(act, sqrsize, vup, vupright, vright, vdownright, vdown, vdownleft, vleft, vupleft, brick_list, up, upright, right, downright, down, downleft, left, upleft);
477 }
478 
479 
480 
481 
482 /*
483  * actor_corners_ex()
484  * Like actor_corners(), but this procedure allows to specify the
485  * collision detectors positions'
486  */
actor_corners_ex(actor_t * act,float sqrsize,v2d_t vup,v2d_t vupright,v2d_t vright,v2d_t vdownright,v2d_t vdown,v2d_t vdownleft,v2d_t vleft,v2d_t vupleft,brick_list_t * brick_list,brick_t ** up,brick_t ** upright,brick_t ** right,brick_t ** downright,brick_t ** down,brick_t ** downleft,brick_t ** left,brick_t ** upleft)487 void actor_corners_ex(actor_t *act, float sqrsize, v2d_t vup, v2d_t vupright, v2d_t vright, v2d_t vdownright, v2d_t vdown, v2d_t vdownleft, v2d_t vleft, v2d_t vupleft, brick_list_t *brick_list, brick_t **up, brick_t **upright, brick_t **right, brick_t **downright, brick_t **down, brick_t **downleft, brick_t **left, brick_t **upleft)
488 {
489     float cd_up[4] = { vup.x-sqrsize , vup.y-sqrsize , vup.x+sqrsize , vup.y+sqrsize };
490     float cd_down[4] = { vdown.x-sqrsize , vdown.y-sqrsize , vdown.x+sqrsize , vdown.y+sqrsize };
491     float cd_left[4] = { vleft.x-sqrsize , vleft.y-sqrsize , vleft.x+sqrsize , vleft.y+sqrsize };
492     float cd_right[4] = { vright.x-sqrsize , vright.y-sqrsize , vright.x+sqrsize , vright.y+sqrsize };
493     float cd_upleft[4] = { vupleft.x-sqrsize , vupleft.y-sqrsize , vupleft.x+sqrsize , vupleft.y+sqrsize };
494     float cd_upright[4] = { vupright.x-sqrsize , vupright.y-sqrsize , vupright.x+sqrsize , vupright.y+sqrsize };
495     float cd_downleft[4] = { vdownleft.x-sqrsize , vdownleft.y-sqrsize , vdownleft.x+sqrsize , vdownleft.y+sqrsize };
496     float cd_downright[4] = { vdownright.x-sqrsize , vdownright.y-sqrsize , vdownright.x+sqrsize , vdownright.y+sqrsize };
497 
498     if(up) *up = brick_at(brick_list, cd_up);
499     if(down) *down = brick_at(brick_list, cd_down);
500     if(left) *left = brick_at(brick_list, cd_left);
501     if(right) *right = brick_at(brick_list, cd_right);
502     if(upleft) *upleft = brick_at(brick_list, cd_upleft);
503     if(upright) *upright = brick_at(brick_list, cd_upright);
504     if(downleft) *downleft = brick_at(brick_list, cd_downleft);
505     if(downright) *downright = brick_at(brick_list, cd_downright);
506 }
507 
508 
509 /*
510  * actor_corners_set_floor_priority()
511  * Which one has the greatest priority: floor (TRUE) or wall (FALSE) ?
512  * Collision-detection routine. See also: brick_at()
513  */
actor_corners_set_floor_priority(int floor)514 void actor_corners_set_floor_priority(int floor)
515 {
516     floor_priority = floor;
517 }
518 
519 
520 /*
521  * actor_corners_restore_floor_priority()
522  * Shortcut to actor_corners_set_floor_priority(TRUE);
523  * TRUE is the default value.
524  */
actor_corners_restore_floor_priority()525 void actor_corners_restore_floor_priority()
526 {
527     actor_corners_set_floor_priority(TRUE);
528 }
529 
530 /*
531  * actor_corners_set_slope_priority()
532  * Which one has the greatest priority: slope (TRUE) or floor (FALSE) ?
533  * Collision-detection routine. See also: brick_at()
534  */
actor_corners_set_slope_priority(int slope)535 void actor_corners_set_slope_priority(int slope)
536 {
537     slope_priority = slope;
538 }
539 
540 
541 /*
542  * actor_corners_restore_slope_priority()
543  * Shortcut to actor_corners_set_slope_priority(TRUE);
544  * TRUE is the default value.
545  */
actor_corners_restore_slope_priority()546 void actor_corners_restore_slope_priority()
547 {
548     actor_corners_set_slope_priority(TRUE);
549 }
550 
551 
552 /*
553  * actor_corners_disable_detection()
554  * Disables the collision detection of a few bricks
555  */
actor_corners_disable_detection(int disable_leftwall,int disable_rightwall,int disable_floor,int disable_ceiling)556 void actor_corners_disable_detection(int disable_leftwall, int disable_rightwall, int disable_floor, int disable_ceiling)
557 {
558     is_leftwall_disabled = disable_leftwall;
559     is_rightwall_disabled = disable_rightwall;
560     is_floor_disabled = disable_floor;
561     is_ceiling_disabled = disable_ceiling;
562 }
563 
564 
565 /*
566  * actor_platform_movement()
567  * Basic platform movement. Returns
568  * a delta_space vector.
569  *
570  * Note: the actor's hot spot must
571  * be defined on its feet.
572  */
actor_platform_movement(actor_t * act,brick_list_t * brick_list,float gravity)573 v2d_t actor_platform_movement(actor_t *act, brick_list_t *brick_list, float gravity)
574 {
575     v2d_t ds = v2d_new(0,0); /* return value */
576     float dt = timer_get_delta(); /* delta_time */
577     float natural_angle = 0; /* default angle (gravity) */
578     float max_y_speed = 480, friction = 0, gravity_factor = 1.0; /* generic modifiers */
579     float diff = MAGIC_DIFF; /* magic hack */
580     v2d_t feet = act->position; /* actor's feet */
581     v2d_t up, upright, right, downright, down, downleft, left, upleft; /* collision detectors (CDs) */
582     brick_t *brick_up, *brick_upright, *brick_right, *brick_downright, *brick_down, *brick_downleft, *brick_left, *brick_upleft; /* bricks detected by the CDs */
583 
584     /* actor's collision detectors */
585     actor_get_collision_detectors(act, diff, &up, &upright, &right, &downright, &down, &downleft, &left, &upleft);
586     actor_handle_collision_detectors(act, brick_list, up, upright, right, downright, down, downleft, left, upleft, &brick_up, &brick_upright, &brick_right, &brick_downright, &brick_down, &brick_downleft, &brick_left, &brick_upleft);
587 
588     /* clouds */
589     actor_handle_clouds(act, diff, &brick_up, &brick_upright, &brick_right, &brick_downright, &brick_down, &brick_downleft, &brick_left, &brick_upleft);
590 
591     /* carrying characters */
592     actor_handle_carrying(act, brick_down);
593     if(act->carried_by != NULL)
594         return v2d_new(0,0);
595 
596     /* wall collisions */
597     actor_handle_wall_collision(act, feet, left, right, brick_left, brick_right);
598 
599     /* y-axis logic */
600     if(brick_down) {
601         act->is_jumping = FALSE;
602         act->ignore_horizontal = FALSE;
603         actor_handle_jumping(act, diff, natural_angle, brick_down, brick_up);
604         actor_handle_slopes(act, brick_down);
605     }
606     else
607         act->angle = natural_angle;
608 
609     /* y-axis movement */
610     ds.y = (fabs(act->speed.y) > EPSILON) ? act->speed.y*dt + 0.5*(gravity*gravity_factor)*(dt*dt) : 0;
611     act->speed.y = min(act->speed.y + (gravity*gravity_factor)*dt, max_y_speed);
612 
613     /* ceiling collision */
614     actor_handle_ceil_collision(act, feet, up, brick_up);
615 
616     /* floor collision */
617     actor_handle_floor_collision(act, diff, natural_angle, &ds, &feet, &friction, brick_downleft, brick_down, brick_downright);
618 
619     /* x-axis movement */
620     ds.x = (fabs(act->speed.x) > EPSILON) ? act->speed.x*dt + 0.5*((1.0-friction)*act->acceleration)*(dt*dt) : 0;
621     actor_handle_acceleration(act, friction, brick_down);
622 
623     /* final adjustments... */
624     if(fabs(act->speed.x) < EPSILON) act->speed.x = ds.x = 0;
625     ds.x += level_brick_move_actor(brick_down,act).x*dt;
626     return ds;
627 }
628 
629 
630 
631 /*
632  * actor_particle_movement()
633  *
634  * Basic particle movement. Returns a
635  * delta_space vector.
636  */
actor_particle_movement(actor_t * act,float gravity)637 v2d_t actor_particle_movement(actor_t *act, float gravity)
638 {
639     float dt = timer_get_delta();
640     v2d_t ds = v2d_new(0,0);
641 
642     /* x-axis */
643     ds.x = act->speed.x*dt;
644 
645     /* y-axis */
646     ds.y = act->speed.y*dt + 0.5*gravity*dt*dt;
647     act->speed.y += gravity*dt;
648 
649     /* done! */
650     return ds;
651 }
652 
653 
654 
655 
656 /*
657  * actor_eightdirections_movement()
658  *
659  * Basic eight directions movement with acceleration.
660  * Returns a delta_space vector.
661  */
actor_eightdirections_movement(actor_t * act)662 v2d_t actor_eightdirections_movement(actor_t *act)
663 {
664     float dt = timer_get_delta();
665     v2d_t ds = v2d_new(0,0);
666 
667     /* input device */
668     if(act->input) {
669         /* x-speed */
670         if(input_button_down(act->input, IB_RIGHT) && !input_button_down(act->input, IB_LEFT))
671             act->speed.x = min(act->speed.x + act->acceleration*dt, act->maxspeed);
672         if(input_button_down(act->input, IB_LEFT) && !input_button_down(act->input, IB_RIGHT))
673             act->speed.x = max(act->speed.x - act->acceleration*dt, -act->maxspeed);
674         if(!input_button_down(act->input, IB_LEFT) && !input_button_down(act->input, IB_RIGHT) && fabs(act->speed.x) > EPSILON) {
675             if(act->speed.x > 0)
676                 act->speed.x = max(act->speed.x - act->acceleration*dt, 0);
677             else
678                 act->speed.x = min(act->speed.x + act->acceleration*dt, 0);
679         }
680 
681         /* y-speed */
682         if(input_button_down(act->input, IB_DOWN) && !input_button_down(act->input, IB_UP))
683             act->speed.y = min(act->speed.y + act->acceleration*dt, act->maxspeed);
684         if(input_button_down(act->input, IB_UP) && !input_button_down(act->input, IB_DOWN))
685             act->speed.y = max(act->speed.y - act->acceleration*dt, -act->maxspeed);
686         if(!input_button_down(act->input, IB_UP) && !input_button_down(act->input, IB_DOWN) && fabs(act->speed.y) > EPSILON) {
687             if(act->speed.y > 0)
688                 act->speed.y = max(act->speed.y - act->acceleration*dt, 0);
689             else
690                 act->speed.y = min(act->speed.y + act->acceleration*dt, 0);
691         }
692     }
693     else
694         act->speed = v2d_new(0,0);
695 
696     /* done! */
697     ds.x = fabs(act->speed.x) > EPSILON ? act->speed.x*dt + 0.5*act->acceleration*dt*dt : 0;
698     ds.y = fabs(act->speed.y) > EPSILON ? act->speed.y*dt + 0.5*act->acceleration*dt*dt : 0;
699     return ds;
700 }
701 
702 
703 /*
704  * actor_bullet_movement()
705  *
706  * Basic bullet movement. Returns a
707  * delta_space vector.
708  *
709  * Bullet movement is a horizontal movement without any gravity effect.  If
710  * a procedure uses this movement it is responsible for destroying the bullet
711  * upon collision.
712  */
actor_bullet_movement(actor_t * act)713 v2d_t actor_bullet_movement(actor_t *act)
714 {
715     float dt = timer_get_delta();
716     v2d_t ds = v2d_new(0,0);
717 
718     /* x-axis */
719     ds.x = act->speed.x*dt;
720 
721     /* y-axis */
722     ds.y = 0.0;
723 
724     /* done! */
725     return ds;
726 }
727 
728 
729 
730 
731 
732 
733 
734 
735 /* platform movement: auxiliary routines */
736 
737 
738 /*
739  * actor_handle_clouds()
740  * Cloud programming (called inside platform
741  * movement methods). Clouds are also known
742  * as 'jump-through' platforms.
743  */
actor_handle_clouds(actor_t * act,float diff,brick_t ** up,brick_t ** upright,brick_t ** right,brick_t ** downright,brick_t ** down,brick_t ** downleft,brick_t ** left,brick_t ** upleft)744 void actor_handle_clouds(actor_t *act, float diff, brick_t **up, brick_t **upright, brick_t **right, brick_t **downright, brick_t **down, brick_t **downleft, brick_t **left, brick_t **upleft)
745 {
746     int i;
747     brick_t **cloud_off[5] = { up, upright, right, left, upleft };
748 
749     /* bricks: laterals and top */
750     for(i=0; i<5; i++) {
751         /* forget bricks */
752         brick_t **brk = cloud_off[i];
753         if(brk && *brk && (*brk)->brick_ref && (*brk)->brick_ref->property == BRK_CLOUD)
754             *brk = NULL;
755     }
756 
757     /* bricks: down, downleft, downright */
758     if(down && *down && (*down)->brick_ref->property == BRK_CLOUD) {
759         float offset = min(15, (*down)->brick_ref->image->h/3);
760         if(!(act->speed.y >= 0 && act->position.y < ((*down)->y+diff+1)+offset)) {
761             /* forget bricks */
762             if(downleft && *downleft == *down)
763                 *downleft = NULL;
764             if(downright && *downright == *down)
765                 *downright = NULL;
766             *down = NULL;
767         }
768     }
769 }
770 
771 /*
772  * actor_get_collision_detectors()
773  * Gets the collision detectors of this actor
774  */
actor_get_collision_detectors(actor_t * act,float diff,v2d_t * up,v2d_t * upright,v2d_t * right,v2d_t * downright,v2d_t * down,v2d_t * downleft,v2d_t * left,v2d_t * upleft)775 void actor_get_collision_detectors(actor_t *act, float diff, v2d_t *up, v2d_t *upright, v2d_t *right, v2d_t *downright, v2d_t *down, v2d_t *downleft, v2d_t *left, v2d_t *upleft)
776 {
777     const int frame_width = actor_image(act)->w, frame_height = actor_image(act)->h;
778     const int slope = !((fabs(act->angle)<EPSILON)||(fabs(act->angle-PI/2)<EPSILON)||(fabs(act->angle-PI)<EPSILON)||(fabs(act->angle-3*PI/2)<EPSILON));
779     const v2d_t feet = act->position;
780     float top, middle, lateral;
781 
782     /* slope hack */
783     if(!slope) { top = 0.7; middle = 0.5; lateral = 0.25; }
784     else       { top = 1.0; middle = 0.7; lateral = 0.25; }
785 
786     /* calculating the collision detectors */
787     *up        = v2d_add ( feet , v2d_rotate( v2d_new(0, -frame_height*top+diff), -act->angle) );
788     *down      = v2d_add ( feet , v2d_rotate( v2d_new(0, -diff), -act->angle) );
789     *left      = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width*lateral+diff, -frame_height*middle), -act->angle) );
790     *right     = v2d_add ( feet , v2d_rotate( v2d_new(frame_width*lateral-diff, -frame_height*middle), -act->angle) );
791     *upleft    = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width*lateral+diff, -frame_height*top+diff), -act->angle) );
792     *upright   = v2d_add ( feet , v2d_rotate( v2d_new(frame_width*lateral-diff, -frame_height*top+diff), -act->angle) );
793     *downleft  = v2d_add ( feet , v2d_rotate( v2d_new(-frame_width*lateral+diff, -diff), -act->angle) );
794     *downright = v2d_add ( feet , v2d_rotate( v2d_new(frame_width*lateral-diff, -diff), -act->angle) );
795 }
796 
797 
798 /*
799  * actor_handle_collision_detectors()
800  * Uses the collision detectors to retrieve *brick_up, ..., *brick_upleft
801  */
actor_handle_collision_detectors(actor_t * act,brick_list_t * brick_list,v2d_t up,v2d_t upright,v2d_t right,v2d_t downright,v2d_t down,v2d_t downleft,v2d_t left,v2d_t upleft,brick_t ** brick_up,brick_t ** brick_upright,brick_t ** brick_right,brick_t ** brick_downright,brick_t ** brick_down,brick_t ** brick_downleft,brick_t ** brick_left,brick_t ** brick_upleft)802 void actor_handle_collision_detectors(actor_t *act, brick_list_t *brick_list, v2d_t up, v2d_t upright, v2d_t right, v2d_t downright, v2d_t down, v2d_t downleft, v2d_t left, v2d_t upleft, brick_t **brick_up, brick_t **brick_upright, brick_t **brick_right, brick_t **brick_downright, brick_t **brick_down, brick_t **brick_downleft, brick_t **brick_left, brick_t **brick_upleft)
803 {
804     const float sqrsize = 2;
805 
806     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);
807 }
808 
809 /*
810  * actor_handle_carrying()
811  * If act is being carried, runs the corresponding logic
812  */
actor_handle_carrying(actor_t * act,brick_t * brick_down)813 void actor_handle_carrying(actor_t *act, brick_t *brick_down)
814 {
815     float dt = timer_get_delta();
816 
817     /* I'm being carried */
818     if(act->carried_by != NULL) {
819         actor_t *car = act->carried_by;
820 
821         /* what should I do? */
822         if((brick_down && brick_down->brick_ref->angle == 0 && (int)car->speed.y >= 5)) {
823             /* put me down! */
824             act->position = act->carried_by->position;
825             act->carried_by->carrying = NULL;
826             act->carried_by = NULL;
827         }
828         else {
829             /* carry me! */
830             act->speed = v2d_new(0,0);
831             act->mirror = car->mirror;
832             act->angle = 0;
833             act->position = v2d_subtract(v2d_add(car->position, v2d_multiply(car->speed,dt)), act->carry_offset);
834         }
835     }
836 }
837 
838 /*
839  * actor_handle_wall_collision()
840  * Handles wall collision
841  */
actor_handle_wall_collision(actor_t * act,v2d_t feet,v2d_t left,v2d_t right,brick_t * brick_left,brick_t * brick_right)842 void actor_handle_wall_collision(actor_t *act, v2d_t feet, v2d_t left, v2d_t right, brick_t *brick_left, brick_t *brick_right)
843 {
844     /* right wall */
845     if(brick_right) {
846         if(brick_right->brick_ref->angle % 90 == 0 && (act->speed.x > EPSILON || right.x > brick_right->x)) {
847             act->speed.x = 0;
848             act->position.x = brick_right->x + (feet.x-right.x);
849         }
850     }
851 
852     /* left wall */
853     if(brick_left) {
854         if(brick_left->brick_ref->angle % 90 == 0 && (act->speed.x < -EPSILON || left.x < brick_left->x+brick_left->brick_ref->image->w)) {
855             act->speed.x = 0;
856             act->position.x = (brick_left->x+brick_left->brick_ref->image->w) + (feet.x-left.x);
857         }
858     }
859 }
860 
861 
862 /*
863  * actor_handle_ceil_collision()
864  * Handles ceiling collision
865  */
actor_handle_ceil_collision(actor_t * act,v2d_t feet,v2d_t up,brick_t * brick_up)866 void actor_handle_ceil_collision(actor_t *act, v2d_t feet, v2d_t up, brick_t *brick_up)
867 {
868     if(brick_up && brick_up->brick_ref->angle % 90 == 0 && act->speed.y < -EPSILON) {
869         act->position.y = (brick_up->y+brick_up->brick_ref->image->h) + (feet.y-up.y);
870         act->speed.y = 10;
871     }
872 }
873 
874 
875 
876 /*
877  * actor_handle_jumping()
878  * Handles the jumping logic
879  */
actor_handle_jumping(actor_t * act,float diff,float natural_angle,brick_t * brick_down,brick_t * brick_up)880 void actor_handle_jumping(actor_t *act, float diff, float natural_angle, brick_t *brick_down, brick_t *brick_up)
881 {
882     int ang = brick_down->brick_ref->angle;
883 
884     if(input_button_down(act->input, IB_FIRE1) && !input_button_down(act->input, IB_DOWN) && !brick_up) {
885         act->angle = natural_angle;
886         act->is_jumping = TRUE;
887         if(ang == 0) {
888             act->speed.y = -act->jump_strength;
889         }
890         else if(ang > 0 && ang < 90) {
891             act->speed.x = min(act->speed.x, -0.7*act->jump_strength);
892             act->speed.y = -0.7*act->jump_strength;
893         }
894         else if(ang == 90) {
895             actor_move(act, v2d_new(20*diff, 0));
896             act->speed.x = min(act->speed.x, -act->jump_strength);
897             act->speed.y = -act->jump_strength/2;
898         }
899         else if(ang > 90 && ang < 180) {
900             actor_move(act, v2d_new(0, -20*diff));
901             act->speed.x = min(act->speed.x, -0.7*act->jump_strength);
902             act->speed.y = act->jump_strength;
903         }
904         else if(ang == 180) {
905             actor_move(act, v2d_new(0, -20*diff));
906             act->speed.x *= -1;
907             act->speed.y = act->jump_strength;
908         }
909         else if(ang > 180 && ang < 270) {
910             actor_move(act, v2d_new(0, -20*diff));
911             act->speed.x = max(act->speed.x, 0.7*act->jump_strength);
912             act->speed.y = act->jump_strength;
913         }
914         else if(ang == 270) {
915             actor_move(act, v2d_new(-20*diff, 0));
916             act->speed.x = max(act->speed.x, act->jump_strength);
917             act->speed.y = -act->jump_strength/2;
918         }
919         else if(ang > 270 && ang < 360) {
920             act->speed.x = max(act->speed.x, 0.7*act->jump_strength);
921             act->speed.y = -0.7*act->jump_strength;
922         }
923     }
924 }
925 
926 
927 /*
928  * actor_handle_slopes()
929  * slopes / speed issues
930  */
actor_handle_slopes(actor_t * act,brick_t * brick_down)931 void actor_handle_slopes(actor_t *act, brick_t *brick_down)
932 {
933     int ang = brick_down->brick_ref->angle;
934     float dt = timer_get_delta();
935     float mytan, factor;
936 
937     if(!act->is_jumping) {
938         if(ang > 0 && ang < 90) {
939             mytan = min(1, tan( ang*PI/180.0 ));
940             if(fabs(act->speed.y) > EPSILON)
941                 act->speed.x = -3*mytan*act->speed.y;
942             else {
943                 factor = (!(act->mirror & IF_HFLIP) ? 0.8 : 2.0) * mytan;
944                 act->speed.x = max(act->speed.x - factor*act->acceleration*dt, -act->maxspeed);
945             }
946         }
947         else if(ang > 270 && ang < 360) {
948             mytan = min(1, -tan( ang*PI/180.0 ));
949             if(fabs(act->speed.y) > EPSILON)
950                 act->speed.x = 3*mytan*act->speed.y;
951             else {
952                 factor = ((act->mirror & IF_HFLIP) ? 0.8 : 2.0) * mytan;
953                 act->speed.x = min(act->speed.x + factor*act->acceleration*dt, act->maxspeed);
954             }
955         }
956     }
957 }
958 
959 
960 /*
961  * actor_handle_acceleration()
962  * Handles x-axis acceleration
963  */
actor_handle_acceleration(actor_t * act,float friction,brick_t * brick_down)964 void actor_handle_acceleration(actor_t *act, float friction, brick_t *brick_down)
965 {
966     float dt = timer_get_delta();
967 
968     if(input_button_down(act->input, IB_LEFT) && !input_button_down(act->input, IB_RIGHT)) {
969         if(!act->ignore_horizontal && !input_button_down(act->input, IB_DOWN)) {
970             act->speed.x = max(act->speed.x - (1.0-friction)*act->acceleration*dt, -act->maxspeed);
971             act->mirror = IF_HFLIP;
972         }
973     }
974     else if(input_button_down(act->input, IB_RIGHT) && !input_button_down(act->input, IB_LEFT)) {
975         if(!act->ignore_horizontal && !input_button_down(act->input, IB_DOWN)) {
976             act->speed.x = min(act->speed.x + (1.0-friction)*act->acceleration*dt, act->maxspeed);
977             act->mirror = IF_NONE;
978         }
979     }
980     else if(brick_down){
981         int signal = 0;
982         int ang = brick_down->brick_ref->angle;
983         float factor;
984 
985         /* deceleration factor */
986         factor = 1.0;
987 
988         /* deceleration */
989         if(ang % 90 == 0) {
990             if( (ang==90 && (act->mirror&IF_HFLIP) && act->speed.x < 0) || ((ang==270) && !(act->mirror&IF_HFLIP) && act->speed.x > 0) ) {
991                 if(act->speed.x > EPSILON) signal = 1;
992                 else if(-act->speed.x > EPSILON) signal = -1;
993                 else signal = 0;
994             }
995             else {
996                 if(act->speed.x > EPSILON) signal = -1;
997                 else if(-act->speed.x > EPSILON) signal = 1;
998                 else signal = 0;
999             }
1000         }
1001         else if((ang > 90 && ang < 180) || (ang > 180 && ang < 270)){
1002             if(act->speed.x > EPSILON) signal = -1;
1003             else if(-act->speed.x > EPSILON) signal = 1;
1004             else signal = 0;
1005         }
1006 
1007         act->speed.x += signal*factor*act->acceleration*dt;
1008     }
1009 
1010 
1011 
1012 }
1013 
1014 
1015 /*
1016  * actor_handle_floor_collision()
1017  * Floor collision
1018  */
actor_handle_floor_collision(actor_t * act,float diff,float natural_angle,v2d_t * ds,v2d_t * feet,float * friction,brick_t * brick_downleft,brick_t * brick_down,brick_t * brick_downright)1019 void actor_handle_floor_collision(actor_t *act, float diff, float natural_angle, v2d_t *ds, v2d_t *feet, float *friction, brick_t *brick_downleft, brick_t *brick_down, brick_t *brick_downright)
1020 {
1021     float dt = timer_get_delta();
1022     int ang;
1023 
1024     if(brick_down && !act->is_jumping) {
1025         ang = brick_down->brick_ref->angle;
1026         act->speed.y = ds->y = 0;
1027         act->angle = ang * PI / 180.0;
1028 
1029         /* 0 floor */
1030         if(ang == 0) {
1031             v2d_t mov = level_brick_move_actor(brick_down, act); /* moveable platforms I */
1032             feet->y = brick_down->y;
1033             *friction = 0;
1034             if(mov.y > EPSILON) /* if the moveable brick is going down... */
1035                 ds->y += mov.y*dt;
1036             else
1037                 act->position.y = feet->y+diff;
1038         }
1039 
1040         /* (0-90) slope */
1041         else if(ang > 0 && ang < 90) {
1042             feet->y = brick_down->y + brick_down->brick_ref->image->h - (act->position.x-brick_down->x)*tan(act->angle);
1043             act->position.y = feet->y+diff;
1044             if(!(act->mirror & IF_HFLIP))
1045                 *friction = 0.2;
1046         }
1047 
1048         /* 90 wall */
1049         else if(ang == 90) {
1050             if(fabs(act->speed.x) > 5) {
1051                 int myang = brick_downright ? brick_downright->brick_ref->angle : -1;
1052                 if(brick_downright && (myang >= ang && myang < ang+90)) {
1053                     feet->y = brick_down->x;
1054                     act->position.x = feet->y+diff;
1055                 }
1056                 else {
1057                     act->angle = natural_angle;
1058                     actor_move(act, v2d_new(6.5*diff, 0));
1059                     act->is_jumping = TRUE;
1060                     act->speed = v2d_new(0, -0.7*fabs(act->speed.x));
1061                 }
1062             }
1063             else {
1064                 act->angle = natural_angle;
1065                 actor_move(act, v2d_new(5*diff, 0));
1066                 act->is_jumping = TRUE;
1067                 act->ignore_horizontal = FALSE;
1068             }
1069             if(!(act->mirror & IF_HFLIP))
1070                 *friction = 1.5;
1071         }
1072 
1073         /* (90-180) slope */
1074         else if(ang > 90 && ang < 180) {
1075             if(fabs(act->speed.x) > 5) {
1076                 feet->y = brick_down->y - (act->position.x-brick_down->x)*tan(act->angle);
1077                 act->position.y = feet->y-diff;
1078             }
1079             else {
1080                 act->angle = natural_angle;
1081                 actor_move(act, v2d_new(0, -15*diff));
1082                 act->is_jumping = TRUE;
1083             }
1084             *friction = 1.5;
1085         }
1086 
1087         /* 180 ceil */
1088         else if(ang == 180) {
1089             if( (act->speed.x > 5 && !(act->mirror & IF_HFLIP)) || (act->speed.x < -5 && act->mirror & IF_HFLIP) ) {
1090                 feet->y = brick_down->y + brick_down->brick_ref->image->h;
1091                 act->position.y = feet->y-diff;
1092             }
1093             else {
1094                 act->angle = natural_angle;
1095                 actor_move(act, v2d_new(0, -20*diff));
1096                 act->is_jumping = TRUE;
1097                 act->speed.x = 0;
1098             }
1099             *friction = 1.2;
1100         }
1101 
1102         /* (180-270) slope */
1103         else if(ang > 180 && ang < 270) {
1104             if(fabs(act->speed.x) > 5) {
1105                 feet->y = brick_down->y + brick_down->brick_ref->image->h - (act->position.x-brick_down->x)*tan(act->angle);
1106                 act->position.y = feet->y-diff;
1107             }
1108             else {
1109                 act->angle = natural_angle;
1110                 actor_move(act, v2d_new(0, -15*diff));
1111                 act->is_jumping = TRUE;
1112             }
1113             *friction = 1.5;
1114         }
1115 
1116         /* 270 wall */
1117         else if(ang == 270) {
1118             if(fabs(act->speed.x) > 5) {
1119                 int myang = brick_downleft ? brick_downleft->brick_ref->angle : -1;
1120                 if(brick_downleft && (myang > ang-90 && myang <= ang)) {
1121                     feet->y = brick_down->x + brick_down->brick_ref->image->w;
1122                     act->position.x = feet->y-diff;
1123                 }
1124                 else {
1125                     act->angle = natural_angle;
1126                     actor_move(act, v2d_new(-6.5*diff, 0));
1127                     act->is_jumping = TRUE;
1128                     act->speed = v2d_new(0, -0.7*fabs(act->speed.x));
1129                 }
1130 
1131             }
1132             else {
1133                 act->angle = natural_angle;
1134                 actor_move(act, v2d_new(-5*diff, 0));
1135                 act->is_jumping = TRUE;
1136                 act->ignore_horizontal = FALSE;
1137             }
1138             if(act->mirror & IF_HFLIP)
1139                 *friction = 1.5;
1140         }
1141 
1142         /* (270-360) slope */
1143         else if(ang > 270 && ang < 360) {
1144             feet->y = brick_down->y - (act->position.x-brick_down->x)*tan(act->angle);
1145             act->position.y = feet->y+diff;
1146             if(act->mirror & IF_HFLIP)
1147                 *friction = 0.2;
1148         }
1149     }
1150 }
1151 
1152 
1153 /* private stuff */
1154 
1155 /* brick_at(): given a list of bricks, returns
1156  * one that collides with the rectangle 'rect'
1157  * PS: this code ignores the bricks that are
1158  * not obstacles */
brick_at(brick_list_t * list,float rect[4])1159 static brick_t* brick_at(brick_list_t *list, float rect[4])
1160 {
1161     brick_t *ret = NULL;
1162     brick_list_t *p;
1163     int deg, inside_region, end = FALSE;
1164     float x, y, mytan, line;
1165     float br[4];
1166 
1167     /* main algorithm */
1168     for(p=list; p && !end; p=p->next) {
1169 
1170         /* I don't care about passable/disabled bricks. */
1171         if(p->data->brick_ref->property == BRK_NONE || !p->data->enabled)
1172             continue;
1173 
1174         /* I don't like clouds. */
1175         if(p->data->brick_ref->property == BRK_CLOUD && (ret && ret->brick_ref->property == BRK_OBSTACLE))
1176             continue;
1177 
1178         /* I don't like moving platforms */
1179         if(p->data->brick_ref->behavior == BRB_CIRCULAR && (ret && ret->brick_ref->behavior != BRB_CIRCULAR) && p->data->y >= ret->y)
1180             continue;
1181 
1182         /* I don't want a floor! */
1183         if(is_floor_disabled && p->data->brick_ref->angle == 0)
1184             continue;
1185 
1186         /* I don't want a ceiling! */
1187         if(is_ceiling_disabled && p->data->brick_ref->angle == 180)
1188             continue;
1189 
1190         /* I don't want a right wall */
1191         if(is_rightwall_disabled && p->data->brick_ref->angle > 0 && p->data->brick_ref->angle < 180)
1192             continue;
1193 
1194         /* I don't want a left wall */
1195         if(is_leftwall_disabled && p->data->brick_ref->angle > 180 && p->data->brick_ref->angle < 360)
1196             continue;
1197 
1198         /* here's something I like... */
1199         br[0] = (float)p->data->x;
1200         br[1] = (float)p->data->y;
1201         br[2] = (float)(p->data->x + p->data->brick_ref->image->w);
1202         br[3] = (float)(p->data->y + p->data->brick_ref->image->h);
1203 
1204         if(bounding_box(rect, br)) {
1205             if(p->data->brick_ref->behavior != BRB_CIRCULAR && (ret && ret->brick_ref->behavior == BRB_CIRCULAR) && p->data->y <= ret->y) {
1206                 ret = p->data; /* I don't like moving platforms. Let's grab a regular platform instead. */
1207             }
1208             else if(p->data->brick_ref->property == BRK_OBSTACLE && (ret && ret->brick_ref->property == BRK_CLOUD)) {
1209                 ret = p->data; /* I don't like clouds. Let's grab an obstacle instead. */
1210             }
1211             else if(p->data->brick_ref->property == BRK_CLOUD && (ret && ret->brick_ref->property == BRK_CLOUD)) {
1212                 /* oh no, two conflicting clouds! */
1213                 if(p->data->y > ret->y)
1214                     ret = p->data;
1215             }
1216             else if(p->data->brick_ref->angle % 90 == 0) { /* if not slope */
1217 
1218 
1219                 if(slope_priority) {
1220                     if(!ret) /* this code priorizes the slopes */
1221                         ret = p->data;
1222                     else {
1223                         if(floor_priority) {
1224                             if(ret->brick_ref->angle % 180 != 0) /* priorizes the floor/ceil */
1225                                 ret = p->data;
1226                         }
1227                         else {
1228                             if(ret->brick_ref->angle % 180 == 0) /* priorizes the walls (not floor/ceil) */
1229                                 ret = p->data;
1230                         }
1231                     }
1232                 }
1233                 else
1234                     ret = p->data; /* priorizes the floors & walls */
1235 
1236 
1237             }
1238             else if(slope_priority) { /* if slope */
1239                 deg = p->data->brick_ref->angle;
1240                 mytan = tan(deg * PI/180.0);
1241                 for(x=rect[0]; x<=rect[2] && !end; x++) {
1242                     for(y=rect[1]; y<=rect[3] && !end; y++) {
1243                         inside_region = FALSE;
1244 
1245                         switch( (int)(deg/90) % 4 ) {
1246                             case 0: /* 1st quadrant */
1247                                 line = br[3] + mytan*(br[0]-x);
1248                                 inside_region = (br[0] <= x && x <= br[2] && line <= y && y <= br[3]);
1249                                 break;
1250                             case 1: /* 2nd quadrant */
1251                                 line = br[3] - mytan*(br[2]-x);
1252                                 inside_region = (br[0] <= x && x <= br[2] && br[1] <= y && y <= line);
1253                                 break;
1254                             case 2: /* 3rd quadrant */
1255                                 line = br[3] - mytan*(br[0]-x);
1256                                 inside_region = (br[0] <= x && x <= br[2] && br[1] <= y && y <= line);
1257                                 break;
1258                             case 3: /* 4th quadrant */
1259                                 line = br[3] + mytan*(br[2]-x);
1260                                 inside_region = (br[0] <= x && x <= br[2] && line <= y && y <= br[3]);
1261                                 break;
1262                         }
1263 
1264                         if(inside_region) {
1265                             ret = p->data;
1266                             end = TRUE;
1267                         }
1268                     }
1269                 }
1270             }
1271         }
1272     }
1273 
1274     return ret;
1275 }
1276 
1277 
1278 /*
1279  * calculate_rotated_boundingbox()
1280  * Calculates the rotated bounding box of a given actor
1281  */
calculate_rotated_boundingbox(const actor_t * act,v2d_t spot[4])1282 void calculate_rotated_boundingbox(const actor_t *act, v2d_t spot[4])
1283 {
1284     float w, h, angle;
1285     v2d_t a, b, c, d, hs;
1286     v2d_t pos;
1287 
1288     angle = -act->angle;
1289     w = actor_image(act)->w;
1290     h = actor_image(act)->h;
1291     hs = act->hot_spot;
1292     pos = act->position;
1293 
1294     a = v2d_subtract(v2d_new(0, 0), hs);
1295     b = v2d_subtract(v2d_new(w, 0), hs);
1296     c = v2d_subtract(v2d_new(w, h), hs);
1297     d = v2d_subtract(v2d_new(0, h), hs);
1298 
1299     spot[0] = v2d_add(pos, v2d_rotate(a, angle));
1300     spot[1] = v2d_add(pos, v2d_rotate(b, angle));
1301     spot[2] = v2d_add(pos, v2d_rotate(c, angle));
1302     spot[3] = v2d_add(pos, v2d_rotate(d, angle));
1303 }
1304 
1305