1 /* xsoldier, a shoot 'em up game with "not shooting" bonus
2 * Copyright (C) 1997 Yuusuke HASHIMOTO <s945750@educ.info.kanagawa-u.ac.jp>
3 * Copyright (C) 2002 Oohara Yuuma <oohara@libra.interq.or.jp>
4 *
5 * This is a copyleft program. See the file LICENSE for details.
6 */
7 /* $Id: game.c,v 1.34 2009/11/08 06:21:24 oohara Exp $ */
8
9 /* DEBUG and JSTK are defined in config.h */
10 #include <config.h>
11 /* pause */
12 #include <unistd.h>
13 /* rand */
14 #include <stdlib.h>
15 /* strlen */
16 #include <string.h>
17
18 #include <stdio.h>
19 /*
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22 #include <X11/keysym.h>
23 */
24
25 #include "image.h"
26 #include "xsoldier.h"
27 #include "player.h"
28 #include "boss.h"
29 #include "enemy.h"
30 #include "extern.h"
31 #include "key.h"
32 #include "star.h"
33 /* ClearEnemyShotManage, ClearManage, DelObj */
34 #include "manage.h"
35 #include "graphic.h"
36 #include "input.h"
37
38 #ifdef JSTK
39 #include "joystick.h"
40 #endif
41
42 /* DamageHit, LargeDamageHit
43 * DrawRec if DEBUG
44 */
45 #include "callback.h"
46 #include "game.h"
47
48 /* local functions */
49 static void DrawInfo(void);
50
51 static void do_actions(void);
52 static void collision_detection(void);
53
54 static int shoot_down_bonus(int percent, int loop, int stage);
55 /*
56 static int perfect_bonus(int loop, int stage);
57 */
58
mainLoop(void)59 int mainLoop(void)
60 {
61 int obj; /* loop counter */
62
63 int ocheck; /* counter for already checked objects */
64
65
66 int oneUp = 0; /* 1up counter */
67
68 /* functions of normal enemies, weak ones first */
69 int (*NewEnemy[])(int x, int y) =
70 {
71 NewEnemy1,NewEnemy2,NewEnemy3,NewEnemy4,NewEnemy5,
72 NewEnemy6,NewEnemy7,NewEnemy8,NewEnemy9
73 };
74
75 /* functions of end-of-stage bosses */
76 int (*NewBoss[])(void) =
77 {
78 NewBoss1,NewBoss2,NewBoss3,NewBoss4,NewBoss5,
79 NewBoss6,NewBoss7,NewBoss8
80 };
81
82 /* number of normal enemies in each stage */
83 int StageObj[] =
84 {
85 80,80,100,100,120,120,140,140
86 };
87
88 char StageName[][16] =
89 {
90 "Stage 1","Stage 2","Stage 3","Stage 4","Stage 5",
91 "Stage 6","Stage 7","Final Stage",
92 "All Clear!"
93 };
94
95 /* add the player ship to the table */
96 NewPlayer(FieldW/2,FieldH - 32);
97 #ifndef HAVE_LIBSDL
98 XFlush(dpy);
99 #endif /* not HAVE_LIBSDL */
100 while (1)
101 {
102 if (waittime && (signal_delivered==0))
103 pause();
104 signal_delivered = 0;
105
106 if (event_handle() == 0)
107 return 0;
108
109 if (keymask & Pause)
110 {
111 if (manage->flag_nopausemessage == False)
112 {
113 draw_string(235, 280, "Pause", strlen("Pause"));
114 draw_string(180, 300, "Press [", strlen("Press ["));
115 draw_string(230, 300, pauseKey, strlen(pauseKey));
116 draw_string(245, 300, "] to resume game",
117 strlen("] to resume game"));
118 }
119 redraw_window();
120 continue;
121 }
122
123 if (manage->Stage>MaxStage && manage->Appear>0)
124 break;
125
126 if (manage->player[0]->Data.used == False)
127 {
128 /* the player is killed */
129 player->Rec[0].loop = manage->Loop;
130 player->Rec[0].stage = manage->Stage;
131 if (player->Ships > 0)
132 {
133 player->Ships--;
134 ClearEnemyShotManage(manage);
135 PlayerLosePower();
136 RestartPlayer(FieldW/2,FieldH - 32);
137 }
138 }
139
140 if (manage->BossKill == True)
141 {
142 /* the boss is killed, or it escaped */
143 manage->Stage++;
144
145 manage->Level += 5;
146
147 if (manage->BossTime >=1)
148 {
149 /* the boss is dead */
150 player->Percent = (manage->StageShotDown/(double)manage->StageEnemy) * 100;
151 /* note that manage->Stage is already incremented */
152 player->Rec[0].score += shoot_down_bonus(player->Percent, manage->Loop, manage->Stage);
153 if (player->Percent >= 100)
154 {
155 /*
156 player->Rec[0].score += perfect_bonus(manage->Loop, manage->Stage);
157 */
158 manage->Level += 7;
159 }
160
161 }
162 else
163 {
164 manage->Level -= 3;
165 }
166 if (manage->Level > MaxLevel)
167 manage->Level = MaxLevel;
168 if (manage->Level < 0)
169 manage->Level = 0;
170
171 if (manage->flag_maxlevel == True)
172 manage->Level = MaxLevel;
173
174 ClearManage(manage);
175
176 ChangeStarParameter(StarPtn1,20);
177 ChangeStarParameter(StarPtn2,25);
178 }
179
180 if (manage->Appear >= 100)
181 {
182 if ((manage->StageEnemy >= StageObj[manage->Stage-1]) && (manage->BossApp==False))
183 {
184 /* the boss appears */
185 if (NewBoss[manage->Stage-1]() != -1)
186 {
187 manage->ZakoApp = False;
188 manage->BossApp = True;
189 manage->StageEnemy++;
190 if (manage->Stage == 8)
191 manage->BossTime = 3000;
192 else
193 manage->BossTime = 2000;
194 }
195 }
196 else if (manage->ZakoApp == True)
197 {
198 /* normal enemy */
199 if (NewEnemy[rand()%(manage->Stage+1)]((rand()%FieldW)+1,0) != -1)
200 {
201 manage->StageEnemy++;
202 }
203 }
204 /* how often normal enemies appear */
205 manage->Appear = 89;
206 }
207 else
208 manage->Appear++;
209
210 do_actions();
211
212 collision_detection();
213
214
215 /* the player gets 1up? */
216 if (player->Rec[0].score >= player->Next)
217 {
218 player->Next += EVERY1UP;
219 player->Ships++;
220
221 /* counter to display 1up message */
222 oneUp = 1;
223 }
224
225 /* draw the window */
226 clear_window();
227
228 /* pixmaps for the background */
229 DrawStar(StarPtn1);
230 DrawStar(StarPtn2);
231 /* pixmaps for objects */
232 for (obj=0,ocheck=0; (obj<manage->EnemyMax && ocheck<manage->EnemyNum); obj++)
233 {
234 if (manage->enemy[obj]->Data.used == True)
235 {
236 manage->enemy[obj]->Realize(&(manage->enemy[obj]->Data),&(manage->enemy[obj]->Grp));
237 ocheck++;
238 }
239 }
240 for (obj=manage->PlayerMax-1,ocheck=0; (obj>=0 && ocheck<manage->PlayerNum); obj--)
241 {
242 if (manage->player[obj]->Data.used == True)
243 {
244 manage->player[obj]->Realize(&(manage->player[obj]->Data),&(manage->player[obj]->Grp));
245 ocheck++;
246 }
247 }
248 if (player->Rec[0].score >= 10000000)
249 player->Rec[0].score = 10000000;
250
251 /* score and other stuff */
252 DrawInfo();
253
254 /* yet more misc stuff */
255 if (oneUp != 0)
256 {
257 if (oneUp%4 > 1)
258 draw_string(440, 620, "1UP", strlen("1UP"));
259 oneUp++;
260 if (oneUp > 50)
261 oneUp = 0;
262 }
263
264 if (manage->player[0]->Data.kill==True && player->Ships==0)
265 draw_string(230, 300, "Game Over", strlen("Game Over"));
266
267 if (manage->Appear < 0)
268 {
269 char Percent[32];
270 char Bonus[32];
271 char Perfect[32];
272
273 if (manage->showShootDown != 0)
274 {
275 /* shoot down bonus message */
276 if (manage->BossTime >= 1)
277 {
278 snprintf(Percent, sizeof(Percent), "shoot down %02d%%",player->Percent);
279 draw_string(210, 370, Percent, strlen(Percent));
280
281
282 snprintf(Bonus, sizeof(Bonus), "Bonus %d pts", shoot_down_bonus(player->Percent, manage->Loop, manage->Stage));
283 draw_string(260 + manage->Appear*3 , 400,
284 Bonus, strlen(Bonus));
285
286 if (player->Percent >= 100)
287 {
288 snprintf(Perfect, sizeof(Perfect), "Perfect!!");
289 draw_string(170 - manage->Appear*3 , 420,
290 Perfect, strlen(Perfect));
291 }
292 }
293 else
294 {
295 snprintf(Percent, sizeof(Percent), "the boss escaped");
296 draw_string(200 ,370 ,Percent, strlen(Percent));
297 }
298
299 }
300 draw_string(230, 320, StageName[manage->Stage-1],
301 strlen(StageName[manage->Stage-1]));
302 }
303 if (manage->Appear == 0)
304 {
305 ChangeStarParameter(StarPtn1,5);
306 ChangeStarParameter(StarPtn2,10);
307 }
308
309 redraw_window();
310 }
311
312 /* ending */
313 return 1;
314 }
315
316 /* show score and other info */
DrawInfo(void)317 static void DrawInfo(void)
318 {
319 static char Score[64];
320 static char Ships[16];
321 static char Stage[16];
322 #ifdef DEBUG
323 static char ObjectP[32];
324 static char ObjectE[32];
325 static char Loop[16];
326 static char Level[16];
327 static char Weapon[16];
328 static char Pow[16];
329 static char Speed[16];
330 static char Enemy[16];
331 static char EnemyKill[16];
332 #endif
333 static char EnemyHP[5];
334 static char BossTime[16];
335
336 int i;
337
338 snprintf(Score, sizeof(Score), "Score % 8d",player->Rec[0].score);
339 snprintf(Stage, sizeof(Stage), "Stage %2d",manage->Stage);
340 snprintf(Ships, sizeof(Ships), "Ships %3d",player->Ships);
341 #ifdef DEBUG
342 snprintf(ObjectE, sizeof(ObjectE), "Enemy Object %3d",manage->EnemyNum);
343 snprintf(ObjectP, sizeof(ObjectP), "Player Object %3d",manage->PlayerNum);
344 snprintf(Loop, sizeof(Loop), "Loop %2d",manage->Loop);
345 snprintf(Level, sizeof(Level), "Level %3d",manage->Level);
346 snprintf(Weapon, sizeof(Weapon), "Weapon %d",manage->player[0]->Data.Cnt[5]);
347 snprintf(Pow, sizeof(Pow), "Pow %2d",manage->player[0]->Data.Cnt[6]);
348 snprintf(Speed, sizeof(Speed), "Speed %2d",manage->player[0]->Data.Speed);
349 snprintf(Enemy, sizeof(Enemy), "Enemy %3d",manage->StageEnemy);
350 snprintf(EnemyKill, sizeof(EnemyKill), "EnemyKill %3d",manage->StageShotDown);
351 #endif
352
353 draw_string(10, 20, Score, strlen(Score));
354 draw_string(430, 20, Stage, strlen(Stage));
355 draw_string(430, 640, Ships, strlen(Ships));
356 #ifdef DEBUG
357 draw_string(10, 40, ObjectE, strlen(ObjectE));
358 draw_string(10, 60, ObjectP, strlen(ObjectP));
359 draw_string(10, 80, Level, strlen(Level));
360 draw_string(10, 100, Enemy, strlen(Enemy));
361 draw_string(10, 120, EnemyKill, strlen(EnemyKill));
362 draw_string(430, 60, Loop, strlen(Loop));
363 draw_string(430, 580, Weapon, strlen(Weapon));
364 draw_string(430, 600, Pow, strlen(Pow));
365 draw_string(430, 620, Speed, strlen(Speed));
366 #endif
367 for (i = 0; i<manage->EnemyMax; i++)
368 if (manage->enemy[i]->Data.used == True)
369 if ((manage->enemy[i]->Hit == EnemyHit1)
370 ||(manage->enemy[i]->Hit == DamageHit)
371 ||(manage->enemy[i]->Hit == LargeDamageHit)
372 ||(manage->enemy[i]->Hit == BossHit1)
373 ||(manage->enemy[i]->Hit == BossHit8))
374 if (manage->enemy[i]->Data.showDamegeTime >0)
375 {
376 snprintf(EnemyHP, 5, "%d",manage->enemy[i]->Data.HP);
377 draw_string(manage->enemy[i]->Data.X, manage->enemy[i]->Data.Y,
378 EnemyHP, strlen(EnemyHP));
379 (manage->enemy[i]->Data.showDamegeTime)--;
380 }
381 if (manage->BossApp == True)
382 {
383 snprintf(BossTime, 16, "Time %4d",manage->BossTime);
384 draw_string(430, 40, BossTime, strlen(BossTime));
385 }
386
387 #ifdef DEBUG
388 for (i = 0; i<manage->EnemyMax; i++)
389 if (manage->enemy[i]->Data.used == True)
390 /* DrawRec does not use arg 2, so NULL will be enough */
391 DrawRec(&(manage->enemy[i]->Data), NULL);
392 for (i = 0; i<manage->PlayerMax; i++)
393 if (manage->player[i]->Data.used == True)
394 /* DrawRec does not use arg 2, so NULL will be enough */
395 DrawRec(&(manage->player[i]->Data), NULL);
396
397 #endif /* DEBUG */
398
399
400
401
402 }
403
do_actions(void)404 static void do_actions(void)
405 {
406 int obj;
407 DelAtt DelFlag;
408
409 for (obj=manage->PlayerMax-1; obj>=0; obj--)
410 manage->player[obj]->Data.shouldAct = manage->player[obj]->Data.used;
411 for (obj=manage->PlayerMax-1; obj>=0; obj--)
412 {
413 if (manage->player[obj]->Data.shouldAct == True)
414 {
415 if (manage->player[obj]->Action(&(manage->player[obj]->Data)) == NullDel)
416 DelObj(manage->player[obj]);
417 }
418 }
419
420 for (obj=0; obj<manage->EnemyMax; obj++)
421 manage->enemy[obj]->Data.shouldAct = manage->enemy[obj]->Data.used;
422 for (obj=0; obj<manage->EnemyMax; obj++)
423 {
424 if (manage->enemy[obj]->Data.used == True)
425 {
426 DelFlag = manage->enemy[obj]->Action(&(manage->enemy[obj]->Data));
427 switch (DelFlag)
428 {
429 case NoneDel:
430 /* do nothing */
431 break;
432 case BossDel:
433 #ifdef DEBUG
434 fprintf(stderr, "DelFlag == BossDel while processing Action: obj = %d\n", obj);
435 #endif
436 if ((DelFlag == BossDel) && (manage->BossTime <= 0))
437 player->Rec[0].score -= manage->enemy[obj]->Data.Point;
438 manage->BossKill = True;
439 /* fall off */
440 case ZakoDel:
441 player->Rec[0].score += manage->enemy[obj]->Data.Point;
442 manage->StageShotDown++;
443 /* fall off */
444 case NullDel:
445 DelObj(manage->enemy[obj]);
446 break;
447 }
448 }
449 }
450 }
451
452
collision_detection(void)453 static void collision_detection(void)
454 {
455 int obj;
456 int target;
457 DelAtt DelFlag;
458
459 for (obj=0; (obj<manage->PlayerMax); obj++)
460 {
461 if (manage->player[obj]->Data.used == False)
462 continue;
463 if (manage->player[obj]->Data.kill == True)
464 continue;
465
466 for (target=0; (target<manage->EnemyMax); target++)
467 {
468 if (manage->enemy[target]->Data.used == False)
469 continue;
470 if (manage->enemy[target]->Data.kill == True)
471 continue;
472
473 if (manage->player[obj]->Data.hitMask & manage->enemy[target]->Data.hitAtt)
474 {
475 if (abs(manage->player[obj]->Data.X-manage->enemy[target]->Data.X)
476 > manage->player[obj]->Data.HarfW+manage->enemy[target]->Data.HarfW)
477 continue;
478 if (abs(manage->player[obj]->Data.Y-manage->enemy[target]->Data.Y)
479 > manage->player[obj]->Data.HarfH+manage->enemy[target]->Data.HarfH)
480 continue;
481
482 /* crash! */
483 /* we call the enemy's Hit first because the Hit of
484 * the player shot 3 changes its Attatck */
485 DelFlag = manage->enemy[target]->Hit(&(manage->enemy[target]->Data),&(manage->player[obj]->Data));
486 switch (DelFlag)
487 {
488 case NoneDel:
489 /* ignore it */
490 break;
491
492 case BossDel:
493 #ifdef DEBUG
494 fprintf(stderr, "DelFlag == BossDel while processing Hit: target = %d, obj = %d\n", target, obj);
495 #endif
496 if ((DelFlag == BossDel) && (manage->BossTime <= 0))
497 player->Rec[0].score -= manage->enemy[target]->Data.Point;
498 manage->BossKill = True;
499 case ZakoDel:
500 player->Rec[0].score += manage->enemy[target]->Data.Point;
501 manage->StageShotDown++;
502 case NullDel:
503 DelObj(manage->enemy[target]);
504 }
505
506 if (manage->player[obj]->Hit(&(manage->player[obj]->Data),&(manage->enemy[target]->Data)) == NullDel)
507 DelObj(manage->player[obj]);
508 }
509 }
510 }
511 }
512
shoot_down_bonus(int percent,int loop,int stage)513 static int shoot_down_bonus(int percent, int loop, int stage)
514 {
515 if (percent < 0)
516 {
517 fprintf(stderr, "shoot_down_bonus: negative percent given, "
518 "assuming 0%%\n");
519 percent = 0;
520 }
521 if (percent > 100)
522 {
523 fprintf(stderr, "shoot_down_bonus: 101+ percent given, "
524 "assuming 100%%\n");
525 percent = 100;
526 }
527 if (percent == 0)
528 return 0;
529
530
531 return (30000 + stage * stage * 1000) * 5 / (105 - percent);
532 }
533
534 /*
535 static int perfect_bonus(int loop, int stage)
536 {
537 return 10000 * stage * loop;
538 }
539 */
540