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