1 /*
2  * background.c - level background/foreground
3  * Copyright (C) 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 <stdlib.h>
21 #include <math.h>
22 #include "background.h"
23 #include "actor.h"
24 #include "../core/sprite.h"
25 #include "../core/video.h"
26 #include "../core/osspec.h"
27 #include "../core/util.h"
28 #include "../core/stringutil.h"
29 #include "../core/logfile.h"
30 #include "../core/timer.h"
31 #include "../core/nanoparser/nanoparser.h"
32 
33 /* forward declarations */
34 typedef struct background_t background_t;
35 typedef struct bgstrategy_t bgstrategy_t;
36 typedef struct bgstrategy_default_t bgstrategy_default_t;
37 typedef struct bgstrategy_circular_t bgstrategy_circular_t;
38 typedef struct bgstrategy_linear_t bgstrategy_linear_t;
39 
40 /* === bgtheme struct (an ordered set of backgrounds) === */
41 struct bgtheme_t {
42     background_t **data; /* array of background_t* */
43     int length; /* length of the data vector */
44 };
45 
46 /* === <<abstract>> bgstrategy_t === */
47 struct bgstrategy_t {
48     background_t *background; /* the background instance we're linked to */
49     void (*update)(bgstrategy_t*); /* update function */
50 };
51 static bgstrategy_t *bgstrategy_delete(bgstrategy_t *strategy); /* class destructor */
52 
53 
54 /* === background struct === */
55 struct background_t {
56     actor_t *actor; /* actor */
57     spriteinfo_t *data; /* this is not stored in the main hash */
58     int repeat_x, repeat_y; /* repeat background? */
59     float zindex; /* 0.0 (far) <= zindex <= 1.0 (near) */
60     bgstrategy_t *strategy; /* Strategy design pattern */
61 };
62 static background_t *background_new(); /* constructor */
63 static background_t *background_delete(background_t *bg); /* destructor */
64 
65 
66 /* === concrete strategies (background behaviors) === */
67 
68 /* default background strategy */
69 struct bgstrategy_default_t {
70     bgstrategy_t base; /* inherits from bgstrategy_t */
71 };
72 static bgstrategy_t *bgstrategy_default_new(background_t *background); /* class constructor */
73 static void bgstrategy_default_update(bgstrategy_t *strategy); /* private class method*/
74 
75 /* circular background strategy (elliptical trajectory) */
76 struct bgstrategy_circular_t {
77     bgstrategy_t base; /* base class */
78     float timer;
79     float amplitude_x, amplitude_y;
80     float angularspeed_x, angularspeed_y;
81     float initialphase_x, initialphase_y;
82 };
83 static bgstrategy_t *bgstrategy_circular_new(background_t *background, float amplitude_x, float amplitude_y, float angularspeed_x, float angularspeed_y, float initialphase_x, float initialphase_y); /* class constructor */
84 static void bgstrategy_circular_update(bgstrategy_t *strategy); /* private class method */
85 
86 /* linear background strategy */
87 struct bgstrategy_linear_t {
88     bgstrategy_t base; /* base class */
89     float speed_x, speed_y;
90 };
91 static bgstrategy_t *bgstrategy_linear_new(background_t *background, float speed_x, float speed_y); /* class constructor */
92 static void bgstrategy_linear_update(bgstrategy_t *strategy); /* private class method */
93 
94 
95 /* === internal methods === */
96 static void sort_backgrounds(bgtheme_t *bgtheme);
97 static int sort_cmp(const void *a, const void *b);
98 static void render(bgtheme_t *theme, v2d_t camera_position, int foreground);
99 static int traverse(const parsetree_statement_t *stmt, void *bgtheme);
100 static int traverse_background_attributes(const parsetree_statement_t *stmt, void *background);
101 static void validate_background(const background_t *bg);
102 
103 
104 /* public methods */
105 
106 /*
107  * background_load()
108  * Loads a background theme from a .bg file
109  */
background_load(const char * file)110 bgtheme_t* background_load(const char *file)
111 {
112     bgtheme_t *bgtheme;
113     parsetree_program_t *tree;
114     char abs_path[1024];
115 
116     logfile_message("background_load('%s')", file);
117     resource_filepath(abs_path, file, sizeof(abs_path), RESFP_READ);
118 
119     bgtheme = mallocx(sizeof *bgtheme);
120     bgtheme->data = NULL;
121     bgtheme->length = 0;
122 
123     tree = nanoparser_construct_tree(abs_path);
124     nanoparser_traverse_program_ex(tree, (void*)bgtheme, traverse);
125     tree = nanoparser_deconstruct_tree(tree);
126 
127     sort_backgrounds(bgtheme);
128     return bgtheme;
129 }
130 
131 
132 
133 /*
134  * background_unload()
135  * Unloads a background theme
136  */
background_unload(bgtheme_t * bgtheme)137 bgtheme_t* background_unload(bgtheme_t *bgtheme)
138 {
139     int i;
140 
141     logfile_message("background_unload()");
142 
143     for(i=0; i<bgtheme->length; i++)
144         bgtheme->data[i] = background_delete(bgtheme->data[i]);
145 
146     free(bgtheme->data);
147     free(bgtheme);
148     return NULL;
149 }
150 
151 
152 
153 /*
154  * background_update()
155  * Updates the background
156  */
background_update(bgtheme_t * bgtheme)157 void background_update(bgtheme_t *bgtheme)
158 {
159     int i;
160     background_t *bg;
161 
162     for(i=0; i<bgtheme->length; i++) {
163         bg = bgtheme->data[i];
164         bg->strategy->update(bg->strategy);
165     }
166 }
167 
168 
169 
170 /*
171  * background_render_bg()
172  * Renders the background
173  */
background_render_bg(bgtheme_t * bgtheme,v2d_t camera_position)174 void background_render_bg(bgtheme_t *bgtheme, v2d_t camera_position)
175 {
176     render(bgtheme, camera_position, FALSE);
177 }
178 
179 /*
180  * background_render_fg()
181  * Renders the foreground
182  */
background_render_fg(bgtheme_t * bgtheme,v2d_t camera_position)183 void background_render_fg(bgtheme_t *bgtheme, v2d_t camera_position)
184 {
185     render(bgtheme, camera_position, TRUE);
186 }
187 
188 
189 
190 
191 
192 
193 /* private methods */
194 
background_new()195 background_t *background_new()
196 {
197     background_t *bg = mallocx(sizeof *bg);
198 
199     bg->actor = actor_create();
200     bg->data = NULL;
201     bg->strategy = NULL;
202     bg->repeat_x = FALSE;
203     bg->repeat_y = FALSE;
204     bg->zindex = 0.0f;
205 
206     return bg;
207 }
208 
background_delete(background_t * bg)209 background_t *background_delete(background_t *bg)
210 {
211     bg->strategy = bgstrategy_delete(bg->strategy);
212     spriteinfo_destroy(bg->data);
213     actor_destroy(bg->actor);
214     free(bg);
215 
216     return NULL;
217 }
218 
bgstrategy_delete(bgstrategy_t * strategy)219 bgstrategy_t *bgstrategy_delete(bgstrategy_t *strategy)
220 {
221     free(strategy);
222     return NULL;
223 }
224 
bgstrategy_default_new(background_t * background)225 bgstrategy_t *bgstrategy_default_new(background_t *background)
226 {
227     bgstrategy_default_t *me = mallocx(sizeof *me);
228     bgstrategy_t *base = (bgstrategy_t*)me;
229 
230     base->background = background;
231     base->update = bgstrategy_default_update;
232 
233     return base;
234 }
235 
bgstrategy_default_update(bgstrategy_t * strategy)236 void bgstrategy_default_update(bgstrategy_t *strategy)
237 {
238     ; /* empty */
239 }
240 
241 
bgstrategy_circular_new(background_t * background,float amplitude_x,float amplitude_y,float angularspeed_x,float angularspeed_y,float initialphase_x,float initialphase_y)242 bgstrategy_t *bgstrategy_circular_new(background_t *background, float amplitude_x, float amplitude_y, float angularspeed_x, float angularspeed_y, float initialphase_x, float initialphase_y)
243 {
244     bgstrategy_circular_t *me = mallocx(sizeof *me);
245     bgstrategy_t *base = (bgstrategy_t*)me;
246 
247     base->background = background;
248     base->update = bgstrategy_circular_update;
249     me->timer = 0.0f;
250     me->amplitude_x = amplitude_x;
251     me->amplitude_y = amplitude_y;
252     me->angularspeed_x = (2.0f * PI) * angularspeed_x;
253     me->angularspeed_y = (2.0f * PI) * angularspeed_y;
254     me->initialphase_x = (initialphase_x * PI) / 180.0f;
255     me->initialphase_y = (initialphase_y * PI) / 180.0f;
256 
257     return base;
258 }
259 
260 
bgstrategy_circular_update(bgstrategy_t * strategy)261 void bgstrategy_circular_update(bgstrategy_t *strategy)
262 {
263     bgstrategy_circular_t *me = (bgstrategy_circular_t*)strategy;
264     background_t *bg = strategy->background;
265     float dt = timer_get_delta();
266     float t, sx, cy;
267 
268     t = (me->timer += dt);
269     sx = sin(me->angularspeed_x * t + me->initialphase_x);
270     cy = cos(me->angularspeed_y * t + me->initialphase_y);
271 
272     /* elliptical trajectory */
273     bg->actor->position.x += (-me->angularspeed_x * me->amplitude_x * sx) * dt;
274     bg->actor->position.y += (me->angularspeed_y * me->amplitude_y * cy) * dt;
275 }
276 
277 
bgstrategy_linear_new(background_t * background,float speed_x,float speed_y)278 bgstrategy_t *bgstrategy_linear_new(background_t *background, float speed_x, float speed_y)
279 {
280     bgstrategy_linear_t *me = mallocx(sizeof *me);
281     bgstrategy_t *base = (bgstrategy_t*)me;
282 
283     base->background = background;
284     base->update = bgstrategy_linear_update;
285     me->speed_x = speed_x;
286     me->speed_y = speed_y;
287 
288     return base;
289 }
290 
291 
bgstrategy_linear_update(bgstrategy_t * strategy)292 void bgstrategy_linear_update(bgstrategy_t *strategy)
293 {
294     bgstrategy_linear_t *me = (bgstrategy_linear_t*)strategy;
295     background_t *bg = strategy->background;
296     float dt = timer_get_delta();
297 
298     bg->actor->position.x += me->speed_x * dt;
299     bg->actor->position.y += me->speed_y * dt;
300 }
301 
302 
render(bgtheme_t * bgtheme,v2d_t camera_position,int foreground)303 void render(bgtheme_t *bgtheme, v2d_t camera_position, int foreground)
304 {
305     int i;
306     v2d_t halfscreen = v2d_new(VIDEO_SCREEN_W/2, VIDEO_SCREEN_H/2);
307     v2d_t topleft = v2d_subtract(camera_position, halfscreen);
308     background_t *bg;
309 
310     for(i=0; i<bgtheme->length; i++) {
311         bg = bgtheme->data[i];
312         if((!foreground && bg->zindex <= 0.5f) || (foreground && bg->zindex > 0.5f)) {
313             bg->actor->position.x += topleft.x * bg->actor->speed.x;
314             bg->actor->position.y += topleft.y * bg->actor->speed.y;
315 
316             actor_render_repeat_xy(bg->actor, halfscreen, bg->repeat_x, bg->repeat_y);
317 
318             bg->actor->position.y -= topleft.y * bg->actor->speed.y;
319             bg->actor->position.x -= topleft.x * bg->actor->speed.x;
320         }
321     }
322 }
323 
sort_backgrounds(bgtheme_t * bgtheme)324 void sort_backgrounds(bgtheme_t *bgtheme)
325 {
326     /* merge_sort is a stable sorting algorithm. stdlib's qsort may not be. */
327     merge_sort(bgtheme->data, bgtheme->length, sizeof *(bgtheme->data), sort_cmp);
328 }
329 
sort_cmp(const void * a,const void * b)330 int sort_cmp(const void *a, const void *b)
331 {
332     const background_t *i = *((const background_t**)a);
333     const background_t *j = *((const background_t**)b);
334 
335     if(fabs(i->zindex - j->zindex) < EPSILON)
336         return 0;
337     else if(i->zindex < j->zindex)
338         return -1;
339     else
340         return 1;
341 }
342 
traverse(const parsetree_statement_t * stmt,void * bgtheme)343 int traverse(const parsetree_statement_t *stmt, void *bgtheme)
344 {
345     const char *identifier;
346     const parsetree_parameter_t *param_list;
347     const parsetree_parameter_t *p1;
348     background_t *bg;
349     bgtheme_t *theme = (bgtheme_t*)bgtheme;
350 
351     identifier = nanoparser_get_identifier(stmt);
352     param_list = nanoparser_get_parameter_list(stmt);
353 
354     if(str_icmp(identifier, "background") == 0) {
355         p1 = nanoparser_get_nth_parameter(param_list, 1);
356 
357         nanoparser_expect_program(p1, "Can't read background. Missing background attributes");
358 
359         bg = background_new();
360         theme->data = reallocx(theme->data, (++(theme->length)) * sizeof(*(theme->data)));
361         theme->data[theme->length-1] = bg;
362         nanoparser_traverse_program_ex(nanoparser_get_program(p1), (void*)bg, traverse_background_attributes);
363         validate_background(bg);
364         actor_change_animation(bg->actor, bg->data->animation_data[0]);
365     }
366     else
367         fatal_error("Can't read background. Unknown identifier: '%s'", identifier);
368 
369     return 0;
370 }
371 
traverse_background_attributes(const parsetree_statement_t * stmt,void * background)372 int traverse_background_attributes(const parsetree_statement_t *stmt, void *background)
373 {
374     const char *identifier;
375     const parsetree_parameter_t *param_list;
376     const parsetree_parameter_t *p1, *p2, *p3, *p4, *p5, *p6, *p7;
377     background_t *bg = (background_t*)background;
378 
379     identifier = nanoparser_get_identifier(stmt);
380     param_list = nanoparser_get_parameter_list(stmt);
381 
382     if(str_icmp(identifier, "initial_position") == 0) {
383         p1 = nanoparser_get_nth_parameter(param_list, 1);
384         p2 = nanoparser_get_nth_parameter(param_list, 2);
385 
386         nanoparser_expect_string(p1, "initial_position must be a pair of numbers");
387         nanoparser_expect_string(p2, "initial_position must be a pair of numbers");
388 
389         bg->actor->spawn_point.x = atof(nanoparser_get_string(p1));
390         bg->actor->spawn_point.y = atof(nanoparser_get_string(p2));
391         bg->actor->position = bg->actor->spawn_point;
392     }
393     else if(str_icmp(identifier, "scroll_speed") == 0) {
394         p1 = nanoparser_get_nth_parameter(param_list, 1);
395         p2 = nanoparser_get_nth_parameter(param_list, 2);
396 
397         nanoparser_expect_string(p1, "scroll_speed must be a pair of numbers");
398         nanoparser_expect_string(p2, "scroll_speed must be a pair of numbers");
399 
400         bg->actor->speed.x = atof(nanoparser_get_string(p1));
401         bg->actor->speed.y = atof(nanoparser_get_string(p2));
402     }
403     else if(str_icmp(identifier, "behavior") == 0) {
404         p1 = nanoparser_get_nth_parameter(param_list, 1);
405         p2 = nanoparser_get_nth_parameter(param_list, 2);
406         p3 = nanoparser_get_nth_parameter(param_list, 3);
407         p4 = nanoparser_get_nth_parameter(param_list, 4);
408         p5 = nanoparser_get_nth_parameter(param_list, 5);
409         p6 = nanoparser_get_nth_parameter(param_list, 6);
410         p7 = nanoparser_get_nth_parameter(param_list, 7);
411 
412         nanoparser_expect_string(p1, "Background behavior must be a string");
413 
414         if(str_icmp(nanoparser_get_string(p1), "DEFAULT") == 0) {
415             if(bg->strategy)
416                 bg->strategy = bgstrategy_delete(bg->strategy);
417             bg->strategy = bgstrategy_default_new(bg);
418         }
419         else if(str_icmp(nanoparser_get_string(p1), "LINEAR") == 0) {
420             nanoparser_expect_string(p2, "Linear background behavior expects a pair of numbers");
421             nanoparser_expect_string(p3, "Linear background behavior expects a pair of numbers");
422 
423             if(bg->strategy)
424                 bg->strategy = bgstrategy_delete(bg->strategy);
425             bg->strategy = bgstrategy_linear_new(
426                 bg,
427                 atof(nanoparser_get_string(p2)),
428                 atof(nanoparser_get_string(p3))
429             );
430         }
431         else if(str_icmp(nanoparser_get_string(p1), "CIRCULAR") == 0) {
432             nanoparser_expect_string(p2, "Circular background behavior expects at least four numbers");
433             nanoparser_expect_string(p3, "Circular background behavior expects at least four numbers");
434             nanoparser_expect_string(p4, "Circular background behavior expects at least four numbers");
435             nanoparser_expect_string(p5, "Circular background behavior expects at least four numbers");
436 
437             if(bg->strategy)
438                 bg->strategy = bgstrategy_delete(bg->strategy);
439             bg->strategy = bgstrategy_circular_new(
440                 bg,
441                 atof(nanoparser_get_string(p2)),
442                 atof(nanoparser_get_string(p3)),
443                 atof(nanoparser_get_string(p4)),
444                 atof(nanoparser_get_string(p5)),
445                 atof(nanoparser_get_string(p6)),
446                 atof(nanoparser_get_string(p7))
447             );
448         }
449         else
450             fatal_error("Unknown background behavior: '%s'", nanoparser_get_string(p1));
451     }
452     else if(str_icmp(identifier, "repeat_x") == 0) {
453         p1 = nanoparser_get_nth_parameter(param_list, 1);
454         nanoparser_expect_string(p1, "repeat_x expects a boolean value");
455         bg->repeat_x = atob(nanoparser_get_string(p1));
456     }
457     else if(str_icmp(identifier, "repeat_y") == 0) {
458         p1 = nanoparser_get_nth_parameter(param_list, 1);
459         nanoparser_expect_string(p1, "repeat_y expects a boolean value");
460         bg->repeat_y = atob(nanoparser_get_string(p1));
461     }
462     else if(str_icmp(identifier, "zindex") == 0) {
463         p1 = nanoparser_get_nth_parameter(param_list, 1);
464         nanoparser_expect_string(p1, "Can't read background attributes: zindex expects a number between 0.0 (far) and 1.0 (near)");
465         bg->zindex = clip(atof(nanoparser_get_string(p1)), 0.0f, 1.0f);
466     }
467     else if(str_icmp(identifier, "sprite") == 0) {
468         p1 = nanoparser_get_nth_parameter(param_list, 1);
469         nanoparser_expect_program(p1, "Can't read background attributes: sprite block expected");
470         if(bg->data != NULL)
471             spriteinfo_destroy(bg->data);
472         bg->data = spriteinfo_create(nanoparser_get_program(p1));
473     }
474     else
475         fatal_error("Can't read background attributes. Unknown identifier: '%s'", identifier);
476 
477     return 0;
478 }
479 
validate_background(const background_t * bg)480 void validate_background(const background_t *bg)
481 {
482     if(bg->data == NULL)
483         fatal_error("Can't read background: no sprite data given");
484 
485     if(bg->strategy == NULL)
486         fatal_error("Can't read background: no behavior given");
487 }
488 
489