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