/* * 大砲ゲーム Ver.6 * by yanagawa@kushiro-ct.ac.jp * 開発履歴 * Ver.6 ソースコードの分割・整理.効果音追加.動作速度一定化. * Ver.5 敵機の種類を複数化した.ソースコードの微修正. * Ver.4 爆発表現とタイトル画面を追加した. * Ver.3 デモンストレーションモードの実験. * Ver.2 敵機と砲弾を複数にした. * Ver.1 敵機を撃ち落とせるようにした. */ #include #include #include "object.h" #include "gameutil.h" #define INTERVAL 20 // 単位時間(ミリ秒) #define GRAVITY 0.01 // 重力加速度 #define N_TYPE 3 // 敵機の種類,don't change #define N_ENEMY 10 // 敵機の最大数 #define N_BULLET 20 // 砲弾の最大数 #define READYINT 10 // 発射の時間間隔(ループ回数) #define BOOMDUR 15 // 爆発の持続時間(ループ回数) #define START_SND "/usr/share/sounds/gaim/redalert.wav" // 開始音 #define SHOT_SND "/usr/share/sounds/k3b_wait_media1.wav" // 発射音 #define BOOM_SND "/usr/share/sounds/gnobots2/splat.wav" // 爆発音 // 物体形状構造体 typedef struct { double sx, sy; // サイズ void (*draw)(double x, double y); // 描画関数ポインタ } Shape; // 敵機構造体 typedef struct { Object obj; Shape shape; } Enemy; /* 敵機の初期化 */ void InitEnemy(Enemy *enemy, double px, double py, double vx, double vy, int life, Shape shape) { InitObject(&(enemy->obj), px, py, vx, vy, shape.sx, shape.sy, life); enemy->shape = shape; } void InitEnemies(Enemy enemy[], int n, double px, double py, double vx, double vy, int life, Shape shape[]) { int i; for (i = 0; i < n; i++) { InitEnemy(&enemy[i], px, py, vx, vy, life, shape[Rand(N_TYPE)]); } } /* 砲弾の初期化 */ void InitBullet(Object *bullet, double px, double py, double vx, double vy, int life) { InitObject(bullet, px, py, vx, vy, 0.0, 0.0, life); } void InitBullets(Object bullet[], int n, double px, double py, double vx, double vy, int life) { int i; for (i = 0; i < n; i++) { InitBullet(&bullet[i], px, py, vx, vy, life); } } /* 砲弾の運動 */ void MoveBullet(Object *obj) { int w, h; getmaxyx(stdscr, h, w); obj->vy += GRAVITY; MoveObject(obj); if ((int)(obj->px) < 0) obj->life = 0; if ((int)(obj->px) >= w) obj->life = 0; if ((int)(obj->py) >= h) obj->life = 0; } /* 敵の運動 */ void MoveEnemy(Enemy *enemy) { int w, h; getmaxyx(stdscr, h, w); MoveObject(&(enemy->obj)); if (enemy->obj.life < 0) enemy->obj.life++; // 爆発中 if ((int)(enemy->obj.px) < -10) enemy->obj.life = 0; if ((int)(enemy->obj.py) < 0) enemy->obj.life = 0; if ((int)(enemy->obj.py) >= h) enemy->obj.life = 0; } /* 弾丸の表示 */ void DrawBullet(Object *bullet) { UseColor(3); mvaddch((int)(bullet->py), (int)(bullet->px), '*'); } void DrawBullets(Object bullet[], int n) { int i; for (i = 0; i < n; i++) { if (bullet[i].life != 0) DrawBullet(&bullet[i]); } } /* 角度の表示 */ void DrawAngle(int y, int x, int angle) { UseColor(7); mvprintw(y, x, "angle : %d deg", angle); } /* 敵機の描画関数 */ void DrawEnemy0(double px, double py) { int x, y; UseColor(2); x = (int)px - 3; y = (int)py; DrawBlocks(y-1, x, " *|| "); DrawBlocks(y , x, "<=====P"); DrawBlocks(y+1, x, " *\\\\ "); }; void DrawEnemy1(double px, double py) { int x, y; UseColor(4); x = (int)px - 4; y = (int)py; DrawBlocks(y-1, x, " -//| /"); DrawBlocks(y , x, "-=======/"); DrawBlocks(y+1, x, " -\\\\| "); }; void DrawEnemy2(double px, double py) { int x, y; UseColor(6); x = (int)px - 6; y = (int)py; DrawBlocks(y-2, x, " /#| "); DrawBlocks(y-1, x, " *|#| /|"); DrawBlocks(y , x, "<###########="); DrawBlocks(y+1, x, " *\\#\\ "); DrawBlocks(y+2, x, " \\#\\ "); }; void DrawBoom(double px, double py, int size) { int x, y; UseColor(1); size = size*10/BOOMDUR; if (size < 4) { x = (int)px - 1; y = (int)py; DrawBlocks(y , x, "( )"); } else if (size < 7) { x = (int)px - 3; y = (int)py; DrawBlocks(y-1, x, " &&& "); DrawBlocks(y , x, " & & "); DrawBlocks(y+1, x, " &&& "); } else { x = (int)px - 5; y = (int)py; DrawBlocks(y-2, x, " &&&&& "); DrawBlocks(y-1, x, " &&& &&& "); DrawBlocks(y , x, "&& &&"); DrawBlocks(y+1, x, " &&& &&& "); DrawBlocks(y+2, x, " &&&&& "); } } /* 敵機の表示 */ void DrawEnemy(Enemy *enemy) { if (enemy->obj.life == 1) { // 機体 enemy->shape.draw(enemy->obj.px, enemy->obj.py); } else { // 爆発 DrawBoom(enemy->obj.px, enemy->obj.py, -enemy->obj.life); } } void DrawEnemies(Enemy enemy[], int n) { int i; for (i = 0; i < n; i++) { if (enemy[i].obj.life != 0) DrawEnemy(&enemy[i]); } } /* ゲームの本体 */ void Game(int mode) { Object bullet[N_BULLET]; Enemy enemy[N_ENEMY]; int h, w; int key, fire, ready, angle; int i, j; char autokey[10] = " - - - k j"; Shape shape[] = { {3.0, 1.0, DrawEnemy0}, {4.0, 1.0, DrawEnemy1}, {5.0, 1.0, DrawEnemy2} }; PlaySnd(START_SND); InitRand(); InitBullets(bullet, N_BULLET, 0.0, 0.0, 0.0, 0.0, 0); InitEnemies(enemy, N_ENEMY, 0.0, 0.0, 0.0, 0.0, 0, shape); angle = 45; ready = 0; timeout(0); while (1) { InitWait(); erase(); getmaxyx(stdscr, h, w); DrawBullets(bullet, N_BULLET); DrawEnemies(enemy, N_ENEMY); DrawAngle(0, 0, angle); refresh(); fire = 0; ; key = getch(); if ((key == EOF) && (mode == 'd')) key = autokey[Rand(10)]; switch (key) { case 'h' : case 'k' : case KEY_LEFT : case KEY_UP : angle += 5; break; // 角度 up case 'l' : case 'j' : case KEY_RIGHT : case KEY_DOWN : angle -= 5; break; // 角度 down case ' ' : fire = 1; break; // 発射 case 'q' : return; // ゲーム中止 } if (angle < 0) angle = 0; if (angle > 90) angle = 90; /* 砲弾発射 */ if (ready <= 0) { if (fire == 1) { for (i = 0; i < N_BULLET; i++) { if (bullet[i].life) continue; PlaySnd(SHOT_SND); InitBullet(&bullet[i], 0.0, (double)(h-1), cos(angle*M_PI/180.0), -sin(angle*M_PI/180.0), 1); ready = READYINT; break; } } } else { ready--; } /* 撃墜 */ for (i = 0; i < N_BULLET; i++) { if (bullet[i].life == 0) continue; for (j = 0; j < N_ENEMY; j++) { if (enemy[j].obj.life > 0) { if (ChkHit(&bullet[i], &enemy[j].obj) != 0) { PlaySnd(BOOM_SND); enemy[j].obj.life = -BOOMDUR; // 爆発 bullet[i].life = 0; break; } } } } /* 砲弾移動 */ for (i = 0; i < N_BULLET; i++) { if (bullet[i].life != 0) MoveBullet(&bullet[i]); } /* 敵機移動 */ for (j = 0; j < N_ENEMY; j++) { if (enemy[j].obj.life != 0) { MoveEnemy(&enemy[j]); } else { InitEnemy(&enemy[j], (double)(w+Rand(10)+5), (double)Rand(h/2), -(Rand(7)+3)*0.1 , 0.0, 1, shape[Rand(N_TYPE)]); } } Wait(INTERVAL); // 動作速度一定版 sleep() } } /* タイトル画面 */ int Title() { int h, w, key; getmaxyx(stdscr, h, w); erase(); UseColor(1); mvaddstr(h/2-4, (w-10)/2, "大砲ゲーム"); UseColor(2); mvaddstr(h/2-2, (w-25)/2, "Push [Space] to start game"); mvaddstr(h/2-1, (w-31)/2, "Push [S] to start in silent mode"); mvaddstr(h/2, (w-21)/2, "Push [D] to start demo"); mvaddstr(h/2+1, (w-16)/2, "Push [Q] to quit"); UseColor(3); mvaddstr(h/2+3, (w-17)/2, "[Space] [↑] [↓]"); mvaddstr(h/2+4, (w-17)/2, " fire up down"); move(0, 0); timeout(-1); key = getch(); return (key); } int main() { int key; initscr(); noecho(); cbreak(); curs_set(0); keypad(stdscr, TRUE); InitColor(COLOR_BLACK); attrset(A_BOLD); // 高輝度表示 while (1) { key = Title(); if (key == 'q') break; if (key == 's') SetSnd(0); else SetSnd(1); Game(key); } endwin(); return (0); }