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