1 /* $Id: gosanpachi.c,v 1.38 2011/08/23 20:06:34 oohara Exp $ */
2 /* [very easy] Gosanpachi */
3 
4 #include <stdio.h>
5 /* malloc, rand */
6 #include <stdlib.h>
7 /* strlen, strcmp */
8 #include <string.h>
9 
10 #include "const.h"
11 #include "tenm_object.h"
12 #include "tenm_graphic.h"
13 #include "tenm_primitive.h"
14 #include "util.h"
15 #include "player-shot.h"
16 #include "tenm_table.h"
17 #include "background.h"
18 #include "chain.h"
19 #include "laser.h"
20 #include "normal-shot.h"
21 #include "tenm_math.h"
22 #include "fragment.h"
23 #include "explosion.h"
24 #include "stage-clear.h"
25 #include "score.h"
26 
27 #include "gosanpachi.h"
28 
29 static int gosanpachi_move(tenm_object *my, double turn_per_frame);
30 static int gosanpachi_hit(tenm_object *my, tenm_object *your);
31 static void gosanpachi_next(tenm_object *my);
32 static int gosanpachi_act(tenm_object *my, const tenm_object *player);
33 static int gosanpachi_draw(tenm_object *my, int priority);
34 static int gosanpachi_green(const tenm_object *my);
35 
36 tenm_object *
gosanpachi_new(void)37 gosanpachi_new(void)
38 {
39   tenm_primitive **p = NULL;
40   tenm_object *new = NULL;
41   int *count = NULL;
42   double *count_d = NULL;
43   double x = (double) (WINDOW_WIDTH / 2);
44   double y = -35.0;
45 
46   p = (tenm_primitive **) malloc(sizeof(tenm_primitive *) * 1);
47   if (p == NULL)
48   {
49     fprintf(stderr, "gosanpachi_new: malloc(p) failed\n");
50     return NULL;
51   }
52 
53   p[0] = (tenm_primitive *) tenm_polygon_new(4,
54                                              x + 48.0, y - 36.0,
55                                              x + 48.0, y + 36.0,
56                                              x - 48.0, y + 36.0,
57                                              x - 48.0, y - 36.0);
58   if (p[0] == NULL)
59   {
60     fprintf(stderr, "gosanpachi_new: cannot set p[0]\n");
61     free(p);
62     return NULL;
63   }
64 
65   count = (int *) malloc(sizeof(int) * 5);
66   if (count == NULL)
67   {
68     fprintf(stderr, "gosanpachi_new: malloc(count) failed\n");
69     (p[0])->delete(p[0]);
70     free(p);
71     return NULL;
72   }
73   count_d = (double *) malloc(sizeof(double) * 4);
74   if (count_d == NULL)
75   {
76     fprintf(stderr, "gosanpachi_new: malloc(count_d) failed\n");
77     free(count);
78     (p[0])->delete(p[0]);
79     free(p);
80     return NULL;
81   }
82 
83   /* list of count
84    * [0] for deal_damage
85    * [1] "damaged" timer
86    * [2] life mode
87    * [3] life timer
88    * [4] "was green when dead" flag
89    */
90   /* list of count_d
91    * [0] speed x
92    * [1] speed y
93    * [2] shoot aim x
94    * [3] shoot aim y
95    */
96 
97   count[0] = 0;
98   count[1] = 0;
99   count[2] = 0;
100   count[3] = 0;
101   count[4] = 0;
102 
103   count_d[0] = 0.0;
104   count_d[1] = (((double) (WINDOW_HEIGHT / 4)) - y) / 60.0;
105   count_d[2] = 0.0;
106   count_d[3] = 1.0;
107 
108   new = tenm_object_new("Gosanpachi", ATTR_BOSS, ATTR_PLAYER_SHOT,
109                         538, x, y,
110                         5, count, 4, count_d, 1, p,
111                         (int (*)(tenm_object *, double))
112                         (&gosanpachi_move),
113                         (int (*)(tenm_object *, tenm_object *))
114                         (&gosanpachi_hit),
115                         (int (*)(tenm_object *, const tenm_object *))
116                         (&gosanpachi_act),
117                         (int (*)(tenm_object *, int))
118                         (&gosanpachi_draw));
119 
120   if (new == NULL)
121   {
122     fprintf(stderr, "gosanpachi_new: tenm_object_new failed\n");
123     if (count_d != NULL)
124       free(count_d);
125     if (count != NULL)
126       free(count);
127     (p[0])->delete(p[0]);
128     free(p);
129     return NULL;
130   }
131 
132   return new;
133 }
134 
135 static int
gosanpachi_move(tenm_object * my,double turn_per_frame)136 gosanpachi_move(tenm_object *my, double turn_per_frame)
137 {
138   double dx_temp;
139   double dy_temp;
140 
141   /* sanity check */
142   if (my == NULL)
143   {
144     fprintf(stderr, "gosanpachi_move: my is NULL\n");
145     return 0;
146   }
147   if (turn_per_frame <= 0.5)
148   {
149     fprintf(stderr, "gosanpachi_move: strange turn_per_frame (%f)\n",
150             turn_per_frame);
151     return 0;
152   }
153 
154   dx_temp = my->count_d[0] / turn_per_frame;
155   dy_temp = my->count_d[1] / turn_per_frame;
156   my->x += dx_temp;
157   my->y += dy_temp;
158   if (my->mass != NULL)
159     tenm_move_mass(my->mass, dx_temp, dy_temp);
160 
161   return 0;
162 }
163 
164 static int
gosanpachi_hit(tenm_object * my,tenm_object * your)165 gosanpachi_hit(tenm_object *my, tenm_object *your)
166 {
167   /* sanity check */
168   if (my == NULL)
169   {
170     fprintf(stderr, "gosanpachi_hit: my is NULL\n");
171     return 0;
172   }
173   if (your == NULL)
174   {
175     fprintf(stderr, "gosanpachi_hit: your is NULL\n");
176     return 0;
177   }
178 
179   if (!(your->attr & ATTR_PLAYER_SHOT))
180     return 0;
181   if (my->count[2] != 1)
182     return 0;
183 
184   deal_damage(my, your, 0);
185   if (gosanpachi_green(my))
186     add_chain(my, your);
187   my->count[1] = 2;
188 
189   if (my->hit_point <= 0)
190   {
191     add_score(5000);
192     set_background(1);
193     gosanpachi_next(my);
194     return 0;
195   }
196 
197   return 0;
198 }
199 
200 static void
gosanpachi_next(tenm_object * my)201 gosanpachi_next(tenm_object *my)
202 {
203   int n;
204 
205   /* sanity check */
206   if (my == NULL)
207   {
208     fprintf(stderr, "gosanpachi_next: my is NULL\n");
209     return;
210   }
211 
212   tenm_table_apply_all((int (*)(tenm_object *, int)) (&delete_enemy_shot), 0);
213   tenm_table_apply_all((int (*)(tenm_object *, int)) (&delete_enemy), 0);
214 
215   /* set "was green" flag before we change the life mode */
216   if (gosanpachi_green(my))
217   {
218     n = 8;
219     my->count[4] = 1;
220   }
221   else
222   {
223     n = 7;
224     my->count[4] = 0;
225   }
226 
227   tenm_table_add(explosion_new(my->x, my->y, 0.0, 0.0,
228                                1, 5000, n, 10.0, 6));
229 
230   my->count[2] = 2;
231   my->count[3] = 0;
232   my->count[1] = 0;
233   my->count_d[0] = 0.0;
234   my->count_d[1] = 0.5;
235 
236   /* don't modify my->attr or my->hit_mask here, or the player shot
237    * may fly through the enemy */
238   tenm_mass_delete(my->mass);
239   my->mass = NULL;
240 }
241 
242 static int
gosanpachi_act(tenm_object * my,const tenm_object * player)243 gosanpachi_act(tenm_object *my, const tenm_object *player)
244 {
245   int t;
246   int i;
247   int theta;
248   double result[2];
249   double v[2];
250 
251   /* sanity check */
252   if (my == NULL)
253   {
254     fprintf(stderr, "gosanpachi_act: my is NULL\n");
255     return 0;
256   }
257   if (player == NULL)
258     return 0;
259 
260   /* for deal_damage */
261   my->count[0] = 0;
262 
263   /* "damaged" count down */
264   if (my->count[1] > 0)
265     (my->count[1])--;
266 
267   (my->count[3])++;
268 
269   /* encounter */
270   if (my->count[2] == 0)
271   {
272     if (my->count[3] >= 60)
273     {
274       my->count[2] = 1;
275       my->count[3] = 0;
276       my->count_d[0] = 0.0;
277       my->count_d[1] = 0.0;
278       return 0;
279     }
280     return 0;
281   }
282 
283   /* dead */
284   if (my->count[2] == 2)
285   {
286     if (gosanpachi_green(my))
287       i = 8;
288     else
289       i = 7;
290 
291     if ((my->count[3] >= 30) && (my->count[3] <= 75)
292         && (my->count[3] % 15 == 0))
293     {
294       theta = rand() % 360;
295       tenm_table_add(explosion_new(my->x + 30.0 * tenm_cos(theta),
296                                    my->y + 30.0 * tenm_sin(theta),
297                                    0.0, 0.0,
298                                    2, 300, i, 5.0, 8));
299     }
300     if (my->count[3] > 120)
301     {
302       tenm_table_add(explosion_new(my->x, my->y,
303                                    0.0, 0.0,
304                                    1, 3000, i, 10.0, 8));
305       tenm_table_add(fragment_new(my->x, my->y, 0.0, 0.0,
306                                   30.0, 100, i, 4.0, 0.0, 16));
307       tenm_table_add(fragment_new(my->x, my->y, 0.0, 0.0,
308                                   50.0, 30, i, 2.5, 0.0, 12));
309 
310       tenm_table_add(stage_clear_new(100));
311       return 1;
312     }
313 
314     return 0;
315   }
316 
317   my->count_d[0] = 0.0;
318   my->count_d[1] = 0.0;
319 
320   /* self-destruction */
321   if ((my->count[2] == 1) && (my->count[3] >= 3900))
322   {
323     set_background(2);
324     clear_chain();
325     gosanpachi_next(my);
326     return 0;
327   }
328 
329   /* shoot */
330   if (my->count[2] != 1)
331     return 0;
332 
333   if (((my->count[3] >= 30) && (my->count[3] < 450))
334       || ((my->count[3] >= 1900) && (my->count[3] < 2320)))
335   {
336     if (my->count[3] < 450)
337       t = my->count[3] - 30;
338     else
339       t = my->count[3] - 1900;
340     if (t % 7 == 0)
341     {
342       tenm_table_add(laser_angle_new(my->x + 48.0, my->y - 36.0,
343                                      4.0 + ((double) (rand() % 8)) / 4.0,
344                                      140 + rand() % 36,
345                                      25.0, 2));
346       tenm_table_add(laser_angle_new(my->x - 48.0, my->y - 36.0,
347                                      4.0 + ((double) (rand() % 8)) / 4.0,
348                                      40 - rand() % 36,
349                                      25.0, 2));
350     }
351     if ((t % 7 == 0) && (t % 21 != 0))
352     {
353       tenm_table_add(normal_shot_point_new(my->x, my->y, 4.0,
354                                            player->x, player->y, 0));
355     }
356   }
357 
358   if (((my->count[3] >= 500) && (my->count[3] < 1214))
359       || ((my->count[3] >= 2370) && (my->count[3] < 3084)))
360   {
361     if (my->count[3] < 1214)
362       t = my->count[3] - 500;
363     else
364       t = my->count[3] - 2370;
365     if (t % 17 == 0)
366     {
367       tenm_table_add(laser_point_new(my->x + 12.0, my->y - 51.0, 4.0,
368                                      my->x, my->y - 46.0,
369                                      25.0, 0));
370       tenm_table_add(laser_point_new(my->x + 84.0, my->y - 21.0, 4.0,
371                                      my->x + 72.0, my->y - 16.0,
372                                      25.0, 0));
373       tenm_table_add(laser_point_new(my->x - 12.0, my->y - 51.0, 4.0,
374                                      my->x, my->y - 46.0,
375                                      25.0, 0));
376       tenm_table_add(laser_point_new(my->x - 84.0, my->y - 21.0, 4.0,
377                                      my->x - 72.0, my->y - 16.0,
378                                      25.0, 0));
379 
380       if (t >= 119)
381       {
382         tenm_table_add(laser_point_new(my->x + 48.0, my->y - 36.0, 4.0,
383                                        my->x + 43.0, my->y - 24.0,
384                                        25.0, 0));
385         tenm_table_add(laser_point_new(my->x - 48.0, my->y - 36.0, 4.0,
386                                        my->x - 43.0, my->y - 24.0,
387                                        25.0, 0));
388       }
389     }
390     if ((t >= 238) && (t % 7 == 0))
391     {
392       tenm_table_add(normal_shot_angle_new(my->x, my->y + 14.0, 4.0,
393                                            23 + rand() % 135, 4));
394       tenm_table_add(normal_shot_angle_new(my->x, my->y + 14.0, 4.0,
395                                            -(23 + rand() % 135), 4));
396     }
397   }
398 
399   if (((my->count[3] >= 1300) && (my->count[3] <= 1840))
400       || ((my->count[3] >= 3170) && (my->count[3] <= 3710)))
401   {
402     if (my->count[3] <= 1840)
403       t = my->count[3] - 1300;
404     else
405       t = my->count[3] - 3170;
406     if (t == 0)
407     {
408       my->count_d[2] = (double) (WINDOW_WIDTH / 2);
409       my->count_d[3] = (double) (WINDOW_HEIGHT / 2);
410       my->count_d[2] += (double) (-5 + rand() % 11);
411       my->count_d[3] += (double) (-5 + rand() % 11);
412     }
413 
414     theta = -t * 2;
415     if (my->count[3] >= 3170)
416       theta *= -1;
417     my->count_d[0] = (((double) (WINDOW_WIDTH / 2)) - 48.0) * tenm_sin(theta);
418     my->count_d[0] += ((double) (WINDOW_WIDTH / 2));
419     my->count_d[0] -= my->x;
420     my->count_d[1] = 0.0;
421     if (t % 7 == 0)
422     {
423       for (i = 0; i < 360; i += 45)
424       {
425         v[0] = my->count_d[2] - my->x;
426         v[1] = my->count_d[3] - my->y;
427         result[0] = v[0];
428         result[1] = v[1];
429         vector_rotate(result, v, i);
430         tenm_table_add(normal_shot_point_new(my->x, my->y, 4.0,
431                                              my->x + result[0],
432                                              my->y + result[1],
433                                              1));
434       }
435     }
436   }
437 
438   return 0;
439 }
440 
441 static int
gosanpachi_draw(tenm_object * my,int priority)442 gosanpachi_draw(tenm_object *my, int priority)
443 {
444   int status = 0;
445   tenm_color color;
446   char temp[32];
447 
448   /* sanity check */
449   if (my == NULL)
450   {
451     fprintf(stderr, "gosanpachi_draw: my is NULL\n");
452     return 0;
453   }
454 
455   /* dead enemy has low priority */
456   if (((my->count[2] <= 1) && (priority != 0))
457       || ((my->count[2] > 1) && (priority != -1)))
458     return 0;
459 
460   /* body */
461   if (gosanpachi_green(my))
462   {
463     if (my->count[1] >= 1)
464       color = tenm_map_color(109, 125, 9);
465     else
466       color = tenm_map_color(61, 95, 13);
467   }
468   else
469   {
470     if (my->count[1] >= 1)
471       color = tenm_map_color(135, 89, 9);
472     else
473       color = tenm_map_color(95, 47, 13);
474   }
475 
476   if (tenm_draw_line((int) (my->x + 12.0), (int) (my->y - 51.0),
477                      (int) (my->x + 84.0), (int) (my->y - 21.0),
478                      1, color) != 0)
479     status = 1;
480   if (tenm_draw_line((int) (my->x - 12.0), (int) (my->y - 51.0),
481                      (int) (my->x - 84.0), (int) (my->y - 21.0),
482                      1, color) != 0)
483     status = 1;
484 
485 
486   /* core */
487   if (tenm_draw_line((int) (my->x + 48.0), (int) (my->y - 36.0),
488                      (int) (my->x + 48.0), (int) (my->y + 36.0),
489                      3, color) != 0)
490     status = 1;
491   if (tenm_draw_line((int) (my->x + 48.0), (int) (my->y + 36.0),
492                      (int) (my->x - 48.0), (int) (my->y + 36.0),
493                      3, color) != 0)
494     status = 1;
495   if (tenm_draw_line((int) (my->x - 48.0), (int) (my->y + 36.0),
496                      (int) (my->x - 48.0), (int) (my->y - 36.0),
497                      3, color) != 0)
498     status = 1;
499   if (tenm_draw_line((int) (my->x - 48.0), (int) (my->y - 36.0),
500                      (int) (my->x + 48.0), (int) (my->y - 36.0),
501                      3, color) != 0)
502     status = 1;
503 
504   /* hit point stat */
505   if (my->count[2] == 1)
506   {
507     sprintf(temp, "%d", my->hit_point);
508     if (draw_string(((int) my->x) - 10, (int) my->y,
509                     temp, (int) strlen(temp)) != 0)
510     {
511       fprintf(stderr, "gosanpachi_draw: draw_string failed\n");
512       status = 1;
513     }
514   }
515 
516   return status;
517 }
518 
519 /* return 1 (true) or 0 (false) */
520 static int
gosanpachi_green(const tenm_object * my)521 gosanpachi_green(const tenm_object *my)
522 {
523   /* sanity check */
524   if (my == NULL)
525     return 0;
526 
527   if ((my->count[2] == 1)
528       && (my->count[3] >= 1300) && (my->count[3] < 3870))
529     return 1;
530   if ((my->count[2] == 2) && (my->count[4] != 0))
531     return 1;
532 
533   return 0;
534 }
535