CODE Python RPG

独学でPythonでRPGを作成する(第19回)先制攻撃・不意打ちを追加

更新日:

こんにちは。
「Pythonでつくる ゲーム開発 入門講座」のChapter11,12(本格RPGを作ろう!全編・後編)を基にRPGを作っていきます。

前回は一定の経験値が貯まるとプレイヤーのレベルを上げるようにしました。

今回は前回の最後に追加した変数actを使って、戦闘の行動順の仕組みを変更していきます。
また、戦闘が始まった時の先制攻撃や不意打ちを追加します。

目次

  1. 変数actの追加
  2. ぼうぎょの行動
  3. 先制攻撃・不意打ちのメッセージ
  4. 先制攻撃・不意打ち
  5. 最後に

変数actの追加

最初に変数actについての変更です。

前回、クラスの変数にself.actを追加しましたが、
コメントの通り、idxの値を入れるので
何の行動(idx)を選択したかを保持します。

class Brave(Chara):
    def __init__(self):
(省略)
        self.act = 0 # 戦闘のコマンド(行動)12:攻撃、18:防御(数字はidxの値)

 

とりあえず、この変数を使って
one_hour_dungeon.pyを変更します。

変更前

def get_battle_turn():
(省略)
    if turn_key is player:
        return turn_key, 12 # idx=12(攻撃)
    else:
        return turn_key, 13
(省略)

def main(): # メイン処理
(省略)
                if COMMAND[btl_cmd_y][btl_cmd_x] == "ぼうぎょ":
                    idx = 18
                    tmr = 0
(省略)
        elif idx == 19: # モンスターの選択
            draw_battle(screen, fontS, turn_obj)
            if battle_select(screen, key) == True:
                set_battle_turn(0)
                idx = 24
                tmr = 0
        elif idx == 24: # ターン確認
            tmr = 0
            turn_obj, idx = get_battle_turn() # 全てidx=12

変更後

def get_battle_turn():
(省略)
    if turn_key is player: # プレイヤーの場合
        return turn_key, player.act # 12をplayer.actに変更
    else: # モンスターの場合
        return turn_key, 13 # モンスターは攻撃のみ(idx=13)
(省略)

def main(): # メイン処理
(省略)
                if COMMAND[btl_cmd_y][btl_cmd_x] == "ぼうぎょ":
                    player.act = 18 # idx=18から変更
                    set_battle_turn(0) # 追加
                    idx = 24 # 追加
                    tmr = 0
(省略)
        elif idx == 19: # モンスターの選択
            draw_battle(screen, fontS, turn_obj)
            if battle_select(screen, key) == True:
                player.act = 12 # 追加
                set_battle_turn(0)
                idx = 24
                tmr = 0
        elif idx == 24: # ターン確認
            tmr = 0
            turn_obj, idx = get_battle_turn() # idx = player.act なので色々な行動が可能!

get_battle_turn関数の戻り値は12の固定にしていたので
「こうげき(idx=12)」のみしか行動できませんでした。

変更前ですと
elif idx == 24
のところで、get_battle_turn関数の戻り値が
全て「こうげき(idx=12)」になるからです。

なので、ここを変数player.actに変更しました。
turn_obj, idx = get_battle_turn() # idx = player.act なので色々な行動が可能!

※モンスターの行動は攻撃のみです。
 そのうち、モンスターも攻撃以外の行動が
 出来るようにしようと思っています。
 return turn_key, 13 # モンスターは攻撃のみ(idx=13) 

これで、idx19の時にモンスターを選択した後に
player.actに12を入れることで
get_battle_turn関数の戻り値はこうげき(idx=12)になり
モンスターに攻撃することになります。

 

 

ぼうぎょの行動

変数actについてもう一つ、ぼうぎょです。

ぼうぎょ(idx=18)を選択した時も
player.actに18を入れることで
get_battle_turn関数の戻り値はぼうぎょ(idx=18)になり
身を守る行動になります。

そして、player.actに18を入れた後にset_battle_turn関数を実行して
モンスターも含めた行動順を決めるようにしました。
変更前では、ぼうぎょの時にset_battle_turn関数を実行していない為
モンスターは行動せずに身を守ってターンが終わっていました。

set_battle_turn関数についてはこちらこちら

def set_battle_turn(num): # 0:通常、1:先制攻撃、2:不意打ち
    global battle_order
    tmp_order = {}
    if num == 0 or num == 1:
        r = random.randint(66, 100)
        tmp_order[player] = int(player.quick * r/100)
    if num == 0 or num == 2:
        for mon in monster:
            r = random.randint(66, 100)
            tmp_order[mon] = int(mon.quick * r/100)
    # [(obj, quick), (ojb, quick), ...]のリストになる
    battle_order = sorted(tmp_order.items(), key=lambda x:x[1], reverse=True)

変更後は、player.actに18を入れた後にset_battle_turn関数を実行するので
ぼうぎょ(idx=18)が実行されるようになりました。

 

あと、防御についてもう一つ、ぼうぎょ(idx=18)は選択した時点で
防御力を2倍にします。(順番が回ってきたら2倍ではない)

なので、一番最初にモンスターに攻撃されたとしても
防御力が2倍の状態で攻撃を受けることになります。

                if COMMAND[btl_cmd_y][btl_cmd_x] == "ぼうぎょ":
                    player.act = 18
                    player.defense() # 追加 選択した時点で防御力2倍にする
                    set_battle_turn(0)
                    idx = 24
                    tmr = 0
(省略)
        elif idx == 18: # ぼうぎょ 順番が回ってきたら戦闘のメッセージを出すだけ、ここで2倍にしない
            draw_battle(screen, fontS, turn_obj)
            if tmr == 1:
                set_message(player.name + "は みをまもっている!")
            if tmr == 5:
                init_message()
                idx = 24 # ターンの確認
                tmr = 0

 

変数actについては以上です。

 

 

先制攻撃・不意打ちのメッセージ

次に、先制攻撃や不意打ちです。

まず、戦闘開始時のメッセージですが、
それぞれ以下の2種類にします。

どちらのメッセージにするかは半々の確率で
表示するようにします。

先制攻撃

 

不意打ち

 

あと、モンスターの種類が複数の場合は「まもののむれ」に変更します。

 

それでは変更していきます。

モンスターが何種類なのかはinit_battle関数から取得するようにします。
この関数は以下を返すようにします。
・1種類ならモンスターの名前
・2種類以上なら「まもののむれ」

変数dic_typにモンスターの種類の数が入っているので
これで判定します。
※一番最後の箇所を追加しました。

def init_battle(bg): # 戦闘に入る準備をする
    global monster
    num_enemy = random.randint(1, 4) # 出現するモンスターの数
    sum_x = 0 # モンスターの表示位置
    list_typ = [] # モンスター番号を入れる
    for _ in range(num_enemy):
        if floor >= 10:
            typ = random.randint(1, 10)
        else:
            typ = random.randint(1, floor+1)
        list_typ.append(typ)
    dic_typ = collections.Counter(list_typ) # 重複を抽出 何種類のモンスターがいるか

    i = 0
    for k, v in dic_typ.items(): # k:typ、v:数(typの種類がvの数だけ存在)
        for j in range(v):
            monster.append(chara.Monster(k))
            if v != 1: # 同じ種類が複数いる時
                monster[i].set_name(j)
            sum_x += monster[i].img.get_width() + 35 # 35はモンスターの間隔
            monster[i].set_y(bg.get_height()*0.6 - monster[i].img.get_height()) # どのモンスターも画面の7割が一番下に揃えられる
            i += 1
    sum_x -= 35 # 最後のモンスターは間隔を開ける必要がない
    x_i_i = bg.get_width()/2 - sum_x/2 # 一番左のモンスターのx座標
    monster[0].set_x(x_i_i) # 1匹目
    for i in range(num_enemy-1): # 2匹目
        x_i_i += monster[i].img.get_width() + 35 # 2匹目以降のモンスターの横幅+間隔 (2匹目は1匹目の横幅+間隔分をずらす)
        monster[i+1].set_x(x_i_i) # 各モンスターのx座標 2匹目以降なので i+1

#####追加#####
    if len(dic_typ) == 1:
        return monster[0].name[:-2] # モンスターが1種類
    else:
        return "まもののむれ" # モンスターが複数種類

 

呼び出す側で戻り値を受け取る為に
変数mon_typを用意します。(モンスターの名前 or 「まもののむれ」)
また、通常 or 先制攻撃 or 不意打ちを入れる変数も用意しておきます。(btl_start)

def main(): # メイン処理
(省略)
    btl_start = 0 # 0:通常、1:先制攻撃、2:不意打ち
    mon_typ = "" # 1種類:モンスターの名前、モンスターの種類が複数:"まもののむれ"
(省略)
        elif idx == 10: # 戦闘開始
            if tmr == 1:
                mon_typ = init_battle(screen) # 戻り値を受け取る

 

この変数mon_typを使って、先制攻撃・不意打ちの
メッセージを表示します。
※「elif tmr == 9:」から追加です。

        elif idx == 10: # 戦闘開始
            if tmr == 1:
                mon_typ = init_battle(screen) # 戻り値を受け取る
                init_message()
            elif tmr <= 4:
                bx = (4-tmr)*220
                by = 0
                screen.blit(imgBtlBG, [bx, by]) # バトル画面を右から左へ挿入していく
                draw_text(screen, "Encounter!", 350, 200, font, WHITE)
            elif tmr == 5:
                set_message(monster[0].name + " が現れた!")
                draw_battle(screen, fontS, turn_obj)
            elif tmr == 6:
                if len(monster) >= 2:
                    set_message(monster[1].name + " が現れた!")
                    draw_battle(screen, fontS, turn_obj)
                    time.sleep(0.3)
            elif tmr == 7:
                if len(monster) >= 3:
                    set_message(monster[2].name + " が現れた!")
                    draw_battle(screen, fontS, turn_obj)
                    time.sleep(0.3)
            elif tmr == 8:
                if len(monster) == 4:
                    set_message(monster[3].name + " が現れた!")
                    draw_battle(screen, fontS, turn_obj)
                    time.sleep(0.3)

#####ここから追加#####
            elif tmr == 9:
                btl_start = random.randint(0, 2) # 通常・先制攻撃・不意打ちをそれぞれ1/3
                if btl_start == 1: # 先制攻撃
                    init_message()
                    if random.randint(0, 1) == 0:
                        set_message("しかし " + mon_typ + "は")
                        set_message("まだ こちらに きづいていない!")
                    else:
                        set_message("しかし " + mon_typ + "は")
                        set_message("おどろき とまどっている!")
                    draw_battle(screen, fontS, turn_obj)
                    time.sleep(0.5)
                elif btl_start == 2: # 不意打ち
                    init_message()
                    if random.randint(0, 1) == 0:
                        set_message(mon_typ + "は")
                        set_message("いきなり おそいかかってきた!")
                    else:
                        set_message(mon_typ + "は")
                        set_message(player.name + "が みがまえるまえに")
                        set_message("おそいかかってきた!")
                    draw_battle(screen, fontS, turn_obj)
                    time.sleep(0.5)

 

randomを使って、通常・先制攻撃・不意打ちを
それぞれ1/3の確率で発生するようにします。
btl_start = random.randint(0, 2)

randint(a, b)は
a <= n <= b
です。
詳細はこちら

先制攻撃・不意打ち、それぞれ2種類のメッセージも
randomを使って半々(1/2)の確率で表示します。
if random.randint(0, 1) == 0:

 

これで戦闘が始まった時、先制攻撃・不意打ちの
メッセージを表示するようになりました。

 

 

先制攻撃・不意打ち

メッセージが表示されるようになったので、
あとは先制攻撃・不意打ちを実行します。

これを実現する関数はこちらで既に作成しました。

def set_battle_turn(num): # 引数numを追加 0:通常、1:先制攻撃、2:不意打ち
    global battle_order
    tmp_order = {}
    if num == 0 or num == 1: # 通常と先制はプレイヤーを追加
        r = random.randint(66, 100)
        tmp_order[player] = int(player.quick * r/100)
    if num == 0 or num == 2: # 通常と不意打ちはモンスターを追加
        for mon in monster:
            r = random.randint(66, 100)
            tmp_order[mon] = int(mon.quick * r/100)
    # [(obj, quick), (ojb, quick), ...]のリストになる
    battle_order = sorted(tmp_order.items(), key=lambda x:x[1], reverse=True)
    print(battle_order)

set_battle_turn関数を呼び出す側で引数を指定すれば
通常・先制攻撃・不意打ちとなります。

で、set_battle_turn関数を実行する場所ですが、
こちらで一度は削除したidx23にします。

あちこちでset_battle_turn関数を実行するより
idx23にまとめたくなったからです。

それでは変更していきます。

        elif idx == 10: # 戦闘開始
(省略)
#####再掲ここから#####
            elif tmr == 9:
                btl_start = random.randint(0, 2)
                btl_start = 2
                if btl_start == 1: # 先制攻撃
                    init_message()
                    if random.randint(0, 1) == 0:
                        set_message("しかし " + mon_typ + "は")
                        set_message("まだ こちらに きづいていない!")
                    else:
                        set_message("しかし " + mon_typ + "は")
                        set_message("おどろき とまどっている!")
                    draw_battle(screen, fontS, turn_obj)
                    time.sleep(0.5)
                elif btl_start == 2: # 不意打ち
                    init_message()
                    if random.randint(0, 1) == 0:
                        set_message(mon_typ + "は")
                        set_message("いきなり おそいかかってきた!")
                    else:
                        set_message(mon_typ + "は")
                        set_message(player.name + "が みがまえるまえに")
                        set_message("おそいかかってきた!")
                    draw_battle(screen, fontS, turn_obj)
                    time.sleep(0.5)
#####再掲ここまで#####

            elif tmr <= 16:
                draw_battle(screen, fontS, turn_obj)
                time.sleep(1)
                init_message()
                if btl_start == 2: # 追加
                    idx = 23 # 不意打ちの時はプレイヤーはコマンドを選択せずにset_battle_turn関数を実行
                else:
                    idx = 11 # 不意打ち以外はプレイヤーのコマンド選択
                tmr = 0
(省略)
        elif idx == 23: # ターンセット 追加(復活)
            set_battle_turn(btl_start) # btl_start 0:通常、1:先制攻撃、2:不意打ち
            btl_start = 0 # 2ターン目からは通常に戻す
            idx = 24
            tmr = 0

 

戦闘が始まった後の行先として、以下を追加しています。
・不意打ち:プレイヤーはコマンドを選択せずにset_battle_turn関数を実行へ(idx23)
・不意打ち以外(通常 or 先制攻撃)
     :プレイヤーのコマンド選択へ(idx11)

if btl_start == 2:
  idx = 23
else:
  idx = 11

 

次にidx==23を復活して、set_battle_turn関数を実行しています。
また、先制攻撃・不意打ちであっても、
2ターン目からは通常に戻します。(btl_start = 0)

elif idx == 23:
  set_battle_turn(btl_start)
  btl_start = 0

 

あと、他にset_battle_turn関数を実行している箇所を
idx23に集めます。
※にげるに失敗した時は不意打ちと同じ効果にする為に
 btl_startに2をセットしています。

                if COMMAND[btl_cmd_y][btl_cmd_x] == "ぼうぎょ":
                    player.act = 18 # get_battle_turnの戻り値idxを18
                    player.defense() # 選択した時点で防御力2倍(ターンが回ってきてからではない)
                    idx = 23 # set_battle_turn(0)から変更
                    tmr = 0

(省略)
        elif idx == 19: # 敵の選択
            draw_battle(screen, fontS, turn_obj)
            if battle_select(screen, key) == True:
                player.act = 12 # get_battle_turnの戻り値idxを12
                idx = 23 # set_battle_turn(0)から変更
                tmr = 0

(省略)
        elif idx == 14: # にげるを選択
(省略)
            if tmr == 10: # 逃げられなかった時
                init_message()
                btl_start = 2 # 不意打ちと同じにする為に2をセット
                idx = 23 # set_battle_turn(2)から変更
                tmr = 0

 
これで先制攻撃、不意打ちを追加することができました。
ご参考までにこちらに動画を載せました。

とういうわけで、今回は以上です!(`・ω・´)

 

 

最後に

今回は先制攻撃・不意打ちの機能を追加しました。

ここしばらく戦闘画面を変更してきましたが、
そろそろ違うことをやりたくなってきました。

ので、次回くらいからマップ作りとか
別のことを進めようかと思っています。

まとめサイトへ

 

 

【ご注意】
プログラムやデータなどは著作権法により保護されています。
著作者の許諾を得ずに、プログラムおよびデータそのものまたは改変したものを
配布したり販売したりすることはできません。
また、これらを利用して発生した損害などに関して、著作者は一切責任を負いません。

 

 

-CODE, Python, RPG
-, , , , , ,

Copyright© kerublog , 2021 All Rights Reserved Powered by STINGER.