C言語でRPGの製作 2

こんにちは

次回の続きでRPGの作成をしています。

今回は基本バトルをプログラムしてみました。

攻撃方法が追加されることで随時修正していく必要があると思いますので、なるべく変更しやすいように意識しています。

実行画面はこんな感じ。

レベルが上がらないと攻撃が当たりにくい...という状況が好きなので、それを取り入れています。

攻撃アクション


コード

中身はこんな感じになっています。

//battle.h #ifndef battle_h
 #ifndef battle_h
#define battle_h

#include "battle.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

//max_ステータス
#define  lv_max 10
#define  hp_max 999
#define  ap_max 99
#define  mp_max 99
#define  sp_max 60
//クリテカル基準値
#define Critical 140
//hitポイントの計算 (0~(140+sp)) 基本最大ポイント200%
#define hit_point_calculation (arc4random_uniform(Critical+sp));
//ダメージ=(攻撃側)(攻撃力*hit)-(防御側)(防御力*防御力補正)
#define damage_calculation (a_ap * hit/100)-(d_dp);

//主人公_デフォルト
struct default_status{
  char name[32];
  int lv;
  int hp;
  int ap;
  int dp;
  int mp;
  int sp;
  int c_hp;
  double c_ap;
  double c_dp;
  double c_sp;
};
extern struct default_status user;
//モンスター
struct monster_status{
  char name[32];
  int hp;
  int ap;
  int dp;
  int mp;
  int sp;
  int ex_p;
  int c_hp;
  double c_ap;
  double c_dp;
  double c_sp;
  
};

//出現中のモンスターのステータス
struct monster {
  int hp;
  int ap;
  int dp;
  int mp;
  int sp;
  int ex_p;
  int c_hp;
  double c_ap;
  double c_dp;
  double c_sp;
};


extern struct monster_status monster[5];
//出現中のモンスターの番号
extern int monster_no;

//battle.cのmain
int battle_main(void);

//初期値の代入
void default_value(void);

//バトルメニュー
int battle_menu(void);
//主人公hp5段回表示
void hp_display(void);
//ダメージ計算 出力 ダメージ
int atk(int,int,int);
//バトル設定 出力 勝利1 敗北−1
int battle(void);
//主人公の攻撃 出力 勝利=1
int user_atk(void);
//モンスターの攻撃 出力 敗北=-1
int monster_atk(void);

//出現するモンスターの調節
void monster_pop(void);
//出現したモンスターステータスを格納
void pop_data_assignment(int no);

#define user_display printf("\n名前:%s [レベル:%d,HP:%d,AP:%d,MP:%d]\n",user.name,user.lv,user.hp,user.ap,user.mp);

#endif /* battle_h */

攻撃用の計算式は変更する可能性が高いので#defineに持ってきています。

//battle.c
#include"battle.h"
//構造体定義
struct default_status user;
struct monster pop;
int battle_main(void){
  
  //初期設定****ゲームスタート時
  //default_value();
  //*********************************
  monster_pop();
  if(battle() == -1) return -1;;
     
  return 0;
}

void default_value(void)
{
  strcpy(user.name, "勇者");
  //デフォルト値
  user.lv = 1;
  user.hp = 10;
  user.ap = 1;
  user.dp = 0;
  user.mp = 1;
  user.sp = 2;
  //補正値
  user.c_ap = 0;
  user.c_dp = 0;
  user.c_sp = 0;
  
  //現在の体力
  user.c_hp = user.hp;

  return;
}

int battle(void)
{
  //1で勝利 -1で敗北
  int vs = 0,user_activity,monster_activity;
  user_activity = 60 - user.sp;
  monster_activity = 60 - monster[monster_no].sp;
  printf("->%sが現れた\n",monster[monster_no].name);
  pop_data_assignment(monster_no);
  int turn = 0;
  while (vs == 0) {
      turn++;
      
      if(turn == user_activity){
          if(battle_menu()==1) return 1;
          printf("\n");
      }
      
      if(turn == monster_activity){
          sleep(1);
          if(monster_atk()==-1)return -1;
          
          printf("\n");
      }
      if(turn == 60)turn = 0;
  }
  
  return 0;
}

int battle_menu(void)
{
  int menu_no;
  
  
  //戦闘メニュー
  printf("HP:");
  hp_display();
  printf("[戦闘メニュ-]\n1:攻撃\n");
  scanf("%d",&menu_no);
  
  switch (menu_no) {
      case 1:
          if( user_atk() == 1)return 1;
          break;
      default:
          break;
  }
     return 0;
}

//user 1 monster 0
int atk(int a_ap ,int sp, int d_dp)
{
  int damage = 0;
  //hitポイント
  int hit = hit_point_calculation
  //実質ダメージ 随時調整
  damage = damage_calculation
  
  //ダメージ計算がマイナスになる場合は0ダメージとする
  if(damage <0)return 0;
  
  return damage;
}

int user_atk(void)
{
  int damage;
  printf("%sの攻撃->",user.name);
  
  damage = atk(user.ap, user.sp, pop.dp);
  pop.c_hp = pop.c_hp - damage;
  
  printf("%dダメージ\n",damage);
  //モンスターの体力が1未満なら勝利
  if(pop.c_hp < 1){
      printf("%sに勝利した\n",monster[monster_no].name);
      return 1;
  }else{
      return 0;
  }
}

int monster_atk(void){
  int damage;
  printf("%sの攻撃->",monster[monster_no].name);
  
  damage = atk(pop.ap, pop.sp, user.dp);
  user.c_hp = user.c_hp - damage;
  
  printf("%dダメージ\n",damage);
  //主人公の体力が1未満なら敗北
  if(user.c_hp < 1){
      printf("%sに敗北した\n",monster[monster_no].name);
      return -1;
  }else{
      return 0;
  }
}


void hp_display(void)
{
  int display;
  display = user.c_hp*100/user.hp;
  
  if(display >= 100)printf("⬛︎⬛︎⬛︎⬛︎⬛︎");
  else if(display >= 80)printf("⬛︎⬛︎⬛︎⬛︎⬜︎");
  else if(display >= 60)printf("⬛︎⬛︎⬛︎⬜︎⬜︎");
  else if(display >= 40)printf("⬛︎⬛︎⬜︎⬜︎⬜︎");
  else if(display >= 20)printf("⬛︎⬜︎⬜︎⬜︎⬜︎");
  else printf("⬜︎⬜︎⬜︎⬜︎⬜︎");
  
  printf("\n");
  
  return;
}

void pop_data_assignment(int no)
{
   pop.hp = monster[no].hp;
   pop.ap = monster[no].ap;
   pop.dp = monster[no].dp;
   pop.mp = monster[no].mp;
   pop.sp = monster[no].sp;
   pop.ex_p = monster[no].ex_p;
   pop.c_hp = monster[no].c_hp;
   pop.c_ap = monster[no].c_ap;
   pop.c_dp = monster[no].c_dp;
   pop.c_sp = monster[no].c_sp;

}

現在攻撃手段は基本攻撃のみですが、魔法とかスキル項目も追加していきたいところです。

次にマップ移動後のモンスター出現を作成。

前回作成したマップと今回作成したバトルを合わせました。

基本バトル

モンスター一覧はmonster.cにまとめて記載しました。

構造体情報をbattle.cと共有しています。

//monster.c
#include "battle.h"
int monster_no;

struct monster_status monster[5] = {
      //name,hp,ap,dp,mp,sp,exp,c_hp,c_ap,c_dp,c_sp
          {"スライム",5,1,0,1,1,10,5,0,0,0},
          {"ゴブリン",10,2,0,1,2,10,10,0,0,0},
          {"オーク",10,2,0,1,2,15,15,0,0,0},
          {"セイレーン",10,1,1,10,5,10,15,0,0,0},
          {"アンデッド",15,5,5,1,1,15,15,0,0,0},
  
  };
  

void monster_pop(void)
{
  int pop;
  //出現するモンスターのレベルを主人公のレベルで調節
  pop = user.lv/2;
  //max10
  monster_no = (arc4random_uniform(1+pop));
  
  pop_data_assignment(monster_no);
}

モンスターの名前はこちらを参考にしました

https://ncode.syosetu.com/n5709da/10/

ステータスはかなり適当なので後ほど変更します。

各モンスターに特有の攻撃なども追加していきたいですね。

map.cはbattle.hをincludeしevent部分にbattle_main()を差し込みとバトルの敗北条件を追加と、

map_display()の条件を一部変更しました。

なんとか形になってきた感じがします。

//map.c
#include"map.h"
#include"battle.h"
#include<stdio.h>
void map_main(void)
{

   //初期設定
   //初期位置
   m_x = 1;
   m_y = 1;
   default_value();

  map_display();
  //イベント値が-1の場合ゲーム終了(ゲームクリア)
  while (event != -1) {
      move_menu();
      event_display();
      //ゲームオーバー時の処理
      if(event == -2){
          printf("[Game over]");
          return;
  }
  }
  
}


int map_display(void)
{
  //移動不可−1 モンスター出現位置0 宝箱3 村2 魔王城4 現在地1
  static int map[7][7] = {{-1,-1,-1,-1,-1,-1,-1},
                          {-1,0,-1,-1,0,3,-1},
                          {-1,0,0,0,3,0,-1},
                          {-1,-1,2,0,0,0,-1},
                          {-1,0,0,-1,0,0,-1},
                          {-1,3,2,0,4,0,-1},
                          {-1,-1,-1,-1,-1,-1,-1}};
  
  //壁であるかどうか
  if( map[m_y][m_x] == -1 & event != 5){
      return -1;
  }
  
  //イベント発生
  event = map[m_y][m_x];
  
  //移動
  map[m_y][m_x] = 1;
  printf("\n");
  
  //マップ表示
  for(int i = 0;i < max_y; i++){
      for (int f = 0; f < max_x; f++) {
          if(map[i][f] == -1)printf("⬛︎");
          if(map[i][f] == 0)printf("⬜︎");
          if(map[i][f] == 1)printf("◉ ");
          if(map[i][f] == 2)printf("村");
          if(map[i][f] == 3)printf("宝");
          if(map[i][f] == 4)printf("魔");
           printf(" ");
      }
      printf("\n");
  }
  
  //通過した場所を移動不可にする
  map[m_y][m_x] = -1;
  
  //次ターンに移動するスペースがあるか確認
  if(map[m_y][m_x+1] >= 0 ||map[m_y+1][m_x] >= 0 ||map[m_y][m_x-1] >= 0 ||map[m_y-1][m_x] >= 0){
     //処理終了
      return 0;
  }else{
      //ゲームオーバー
      event = -2;
      return -2;
  }
}

void move_menu(void)
{
  int menu_no;
  while (1) {
  //値を手に入れる
  int x = m_x,y = m_y;
  
  //移動メニュー
  printf("\n[移動メニュー]\n 1:← 2:↑ 3:→ 4:↓\n");
  scanf("%d",&menu_no);
  
  //移動値
  if(menu_no == 1)m_x--;
  if(menu_no == 2)m_y--;
  if(menu_no == 3)m_x++;
  if(menu_no == 4)m_y++;
  
  //移動可能であることの確認
  if(m_x >= 0 && x <= max_x&& m_y >= 0 && y <= max_y){
  //壁に当たった場合
      if(map_display() != -1)return ;
      printf("壁です");
  //ゲーム続行不可の場合
  }else if(event == -2){
      return;
  }else{}
      //移動不可時の処理(移動前の位置に戻す)
      m_x = x;
      m_y = y;
      map_display();
     
  }

}

void event_display(void)
{
  switch (event) {
      case 0:
               //基本バトルの敗北
          if(battle_main()==-1){
              event = -2;
              return;
          //勝利
          }else{
           event=5;
          break;
          }     
      case 2:
          printf("村に到着しました");
          break;
      case 3:
          printf("宝箱を手に入れました");
          break;
      case 4:
          printf("魔王が現れた");
          event = -1;
      default:
          break;
  }
  if(event == 5)map_display();
  event = 0;
}

ただ、このままだと主人公のレベルはずっと1のままなのでそろそろ主人公の育成機能を追加していきたいと思います。

この記事が気に入ったらサポートをしてみませんか?