CODE Python RPG

独学でPythonでRPGを作成する(第57回)戦闘(もろもろ修正)

投稿日:

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

前回は戦闘に関する修正で、攻撃対象モンスターの変更などいくつか修正しました。

今回も戦闘に関して修正していきます。
仲間のキャラが生存しているか、そうでないか、で
動作が変わるように修正します。

その他にもいくつかバグを修正します。

目次

  1. 回復呪文の対象
  2. しんだキャラの行動
  3. モンスターの攻撃対象
  4. ターン内の行動
  5. 回復対象の変更
  6. 経験値
  7. にげる
  8. 最後に

回復呪文の対象

仲間が回復などの呪文で
対象を選択する時に
名前を点滅させています。
※以下画像だと「ハロ2」

ただ、呪文を覚えていない
キャラの名前が点滅しなかったので
これを修正します。

変更前

def draw_battle(bg, fnt, obj, player): # 戦闘画面の描画 obj:戦闘で行動中のオブジェクト
(省略)

    for i, p in enumerate(player):
        x_i_t.append(x_i + x_b*i + 10) # テキストのx座標の開始位置
        # 名前の表示(回復呪文の対象を選択中の時は点滅)
        spell_target_no = -1
        if len(p.mas_spell) != 0:
            spell_target_no = chara.get_spell_target(p.mas_spell[sel_spell_i]) # 呪文の対象(0:味方単体,1:味方全員,2:敵単体,3:敵グループ,4:敵全体)
        if idx == 25 and ((spell_target_no == 0 and i == spell_target_i) or spell_target_no == 1): # 呪文の対象の選択中,呪文の対象が味方なら、対象が単体の時は対象のみ、対象が全員の時は全員
            draw_text(bg, "{}".format(p.name), x_i_t[i], y_i_t, fnt, BLINK[tmr%6])
if len(p.mas_spell) != 0:

ですと、呪文を使うキャラの
オブジェクトになって
いませんでした。
※呪文を覚えていないキャラだと
 spell_target_noは常に-1です。

なので、「p.」ではなく
「player[btl_player].」に
修正します。

変更後

def draw_battle(bg, fnt, obj, player): # 戦闘画面の描画 obj:戦闘で行動中のオブジェクト
(省略)

    spell_target_no = -1
    if len(player[btl_player].mas_spell) != 0:
        spell_target_no = chara.get_spell_target(player[btl_player].mas_spell[sel_spell_i]) # 呪文の対象(0:味方単体,1:味方全員,2:敵単体,3:敵グループ,4:敵全体)
    for i, p in enumerate(player):
        x_i_t.append(x_i + x_b*i + 10) # テキストのx座標の開始位置
        # 名前の表示(回復呪文の対象を選択中の時は点滅)
        if idx == 25 and ((spell_target_no == 0 and i == spell_target_i) or spell_target_no == 1): # 呪文の対象の選択中,呪文の対象が味方なら、対象が単体の時は対象のみ、対象が全員の時は全員
            draw_text(bg, "{}".format(p.name), x_i_t[i], y_i_t, fnt, BLINK[tmr%6])

ついでにfor文の中である必要もないので
for文の外にしました。

 

 

しんだキャラの行動

しんだ(しんでいる)キャラは
行動しないように修正します。

まず、コマンドを
選択しないようにします。

変更前

def main(screen, clock, font, fontS, player, area):
(省略)

        elif idx == 11: # プレイヤーのターン(入力待ち)
(省略)

            if not player[btl_player].flag_sleep: # 眠っていないならコマンド選択 眠っているとactは28にセットされている

                draw_battle(screen, fontS, turn_obj, player)
                if battle_command(key) == True:
                    if COMMAND[btl_cmd_y][btl_cmd_x] == "こうげき":
                        idx = 19
                        tmr = 0

変更後

def main(screen, clock, font, fontS, player, area):
(省略)

        elif idx == 11: # プレイヤーのターン(入力待ち)
(省略)

            # 眠っていないならコマンド選択 眠っているとactは28にセットされている
            if player[btl_player].hp == 0 or player[btl_player].flag_sleep:
                idx = 23
            else:
                draw_battle(screen, fontS, turn_obj, player)
                if battle_command(key) == True:
                    if COMMAND[btl_cmd_y][btl_cmd_x] == "こうげき":
                        idx = 19
                        tmr = 0
if player[btl_player].hp == 0 or player[btl_player].flag_sleep:
    idx = 23

生存しているかを
条件に追加しました。

 

次に、行動しないように
オブジェクトを外します。

def set_battle_turn(num, player): # 0:通常、1:先制攻撃、2:不意打ち
    global battle_order
    tmp_order = {}
    tmp_player = [p for p in player if p.hp > 0] # 追加
    # 素早さの順に並べる(戦闘順)
    if num == 0 or num == 1:
        for p in tmp_player: # player → tmp_playerに変更
            r = random.randint(66, 100)
            tmp_order[p] = int(p.quick * r/100)
(省略)
tmp_player = [p for p in player if p.hp > 0] # 追加

生存しているキャラに絞るため
この一行を追加しました。
※リスト内包表記はこちら
※今回、このリスト内包表記が
 たくさん出てきます。

インスタンス変数のactに値が
入っていると、
しんでいるキャラも
行動してしまうので、追加しました。

 

 

モンスターの攻撃対象

モンスターの攻撃対象について
しんでいるキャラには
攻撃しないようにします。

また、キャラの並び順で
前にいるキャラほど
攻撃を受ける確率を高くします。

変更前

def main(screen, clock, font, fontS, player, area):
(省略)

        elif idx == 13: # 敵のターン、敵の攻撃
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                p_target = player[random.randint(0, len(player)-1)] # モンスターの攻撃対象はランダム

攻撃対象をランダムで選択していますが
全てのキャラが対象になっていました。

変更後

def main(screen, clock, font, fontS, player, area):
(省略)

        elif idx == 13: # 敵のターン、敵の攻撃
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                tmp_player = [p for p in player if p.hp > 0] # 生存キャラ
                l_player = []
                for i, p in enumerate(reversed(tmp_player)): # 前にいるキャラほど攻撃を受けやすくする(前ほど確率をあげる)
                    l_player += [p] * (i+1)
                p_target = random.choice(l_player) # 生存キャラの中からモンスターの攻撃対象をランダム
tmp_player = [p for p in player if p.hp > 0] # 生存キャラ

攻撃対象を生存している
キャラにしぼります。

l_player = []
for i, p in enumerate(reversed(tmp_player)): # 前にいるキャラほど攻撃を受けやすくする(前ほど確率をあげる)
    l_player += [p] * (i+1)

前にいるキャラほど攻撃を
受けやすくします。

前からのオブジェクトを
p1,p2,p3,p4
とすると、l_playerには
以下の値が入ります。

l_player = [p4, p3, p3, p2, p2, p2, p1, p1, p1, p1]
※enumerate や reversedはこちら

前にいるキャラの
オブジェクトを多くして、
それぞれ選択される
可能性を変えています。

 

 

ターン内の行動

モンスターからの攻撃で
味方キャラがしんだ時に
そのキャラは行動しないように
修正します。

まず、キャラが生存しているかを
確認する関数check_playerを作成します。

def check_player(obj):
    global idx, tmr
    if obj.hp <= 0:
        obj.hp = 0
        return True
    else:
        return False

生存していなければ、True
生存していれば、False
です。

攻撃されたキャラが
しんだら行動しないようにします。

def check_player(obj):
    global idx, tmr
    if obj.hp <= 0:
        obj.hp = 0
        del_battle_turn(obj) # ターンオブジェクトから消す
        return True
    else:
        return False

def del_battle_turn(obj1): # ターン内に気絶したオブエクトを削除
    for i, obj2 in enumerate(battle_order):
        if obj1 is obj2[0]: # obj2[0]オブジェクト、obj2[1]は素早さ
            battle_order.pop(i) # i行目を削除

del_battle_turn関数を使って
削除します。

del_battle_turn関数は
モンスターの生存確認をする
check_monster関数でも
利用していました。

 

もう一つ、全員生存しているか
確認する条件を追加します。
※全員しんでいれば敗北にします。

def check_player(obj, player):
    global idx, tmr
    if obj.hp <= 0:
        obj.hp = 0
        tmp_player = [p for p in player if p.hp > 0]
        if not tmp_player: # not monsterがTrueの場合、リストは空
            idx = 15 # 敗北
            tmr = 0
        return True
    else:
        return False
tmp_player = [p for p in player if p.hp > 0]
    if not tmp_player: # not monsterがTrueの場合、リストは空
        idx = 15 # 敗北
        tmr = 0

tmp_playerが空の場合
生存キャラがいないので
全滅ということになります。

ちなみに今までは
攻撃されたキャラがしんだら
敗北になっていました。

変更前

def main(screen, clock, font, fontS, player, area):
(省略)

        elif idx == 13: # 敵のターン、敵の攻撃
            draw_battle(screen, fontS, turn_obj, player)
(省略)

            if tmr == 15:
                p_target.hp = p_target.hp - dmg
                if p_target.hp <= 0:
                    p_target.hp = 0
                    idx = 15 # 敗北
                    tmr = 0

他に生存しているキャラが
いたとしても敗北になって
しまっていました。

 

このcheck_player関数を
使ってidx13を修正します。

def main(screen, clock, font, fontS, player, area):
(省略)

        elif idx == 13: # 敵のターン、敵の攻撃
            draw_battle(screen, fontS, turn_obj, player)
(省略)

            if tmr == 15:
                p_target.hp = p_target.hp - dmg
                if not check_player(p_target, player):
                    if dmg != 0 and p_target.flag_sleep: # 生きている かつ 攻撃を受けた かつ 眠っている
                        if random.randint(0, 99) < 55:
                            p_target.invalid_sleep()
                            set_message(p_target.name + " はめをさました!")
(省略)
if not check_player(p_target, player):

で、生存しているかどうかを
確認しています。

 

 

回復対象の変更

ターン内で回復対象のキャラが
しんだ場合は、対象を変更するように
修正します。

def main(screen, clock, font, fontS, player, area):
(省略)

        elif idx == 26: # 呪文発動
            draw_battle(screen, fontS, turn_obj, player)
(省略)

                    if spell_target_no == 0: # 呪文の対象が味方単体
                        if turn_obj.target.hp == 0: # 回復対象がしんだ場合
                            tmp_player = [p for p in player if p.hp != 0 and p.hp != p.maxhp] # 全快ではないキャラ
                            if not tmp_player: # 全員全快なら
                                tmp_player = [p for p in player if p.hp > 0] # 生存キャラ
                            p_target = random.choice(tmp_player) # 生存キャラの中から回復対象をランダム
                        else: # 回復対象が生存の場合はそのまま回復
                            p_target = turn_obj.target
                        spell_point, spell_message = turn_obj.use_spell(turn_obj.spell, p_target, spell_target_no)
                        if spell_point != -1: # 回復系、-1は補助系
                            p_target.hp += spell_point # turn_obj → p_target
                            if p_target.hp > p_target.maxhp: # turn_obj → p_target
                                p_target.hp = p_target.maxhp # turn_obj → p_target
                        set_message(spell_message)
if turn_obj.target.hp == 0: # 回復対象がしんだ場合
    tmp_player = [p for p in player if p.hp != 0 and p.hp != p.maxhp] # 全快ではないキャラ

回復対象がしんだ場合は
他のキャラのうち
生存していて、全快ではない
キャラを確認します。

if not tmp_player: # 全員全快なら
    tmp_player = [p for p in player if p.hp > 0] # 生存キャラ

全員全快の場合は
単に生存しているキャラを
確認します。

p_target = random.choice(tmp_player) # 生存キャラの中から回復対象をランダム

条件を満たすキャラを確認したら
その中からランダムで対象を
選択するようにします。

※対象キャラのオブジェクトを
 格納する変数をp_targetに
 変更した関係で
 turn_obj → p_target
 に変更しています。

 

また、対象が味方全員の呪文の場合
生存していることを確認する条件を
追加します。

def main(screen, clock, font, fontS, player, area):
(省略)

        elif idx == 26: # 呪文発動
            draw_battle(screen, fontS, turn_obj, player)
(省略)

                    elif spell_target_no == 1:
                        for i, p in enumerate(player):
                            if p.hp == 0: # 追加
                                continue # 追加
                            spell_point, spell_message = turn_obj.use_spell(turn_obj.spell, p, spell_target_no)
                            set_message(spell_message)
if p.hp == 0:
    continue

の2行を追加しました。

しんでいるキャラは
飛ばします。

 

 

経験値

戦闘勝利後に
獲得する経験値について
生存しているキャラのみが
獲得するように修正します。

def main(screen, clock, font, fontS, player, area):
(省略)

        elif idx == 16: # 勝利
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                if len(dead_monster) != 1: # 1匹の時はいらない
                    set_message(mon_typ + " をやっつけた!")
                tmp_player = [p for p in player if p.hp > 0] # 生存者に経験値
                btl_exp = math.ceil(btl_exp/len(tmp_player)) # player → tmp_player
                for p in tmp_player: # player → tmp_player
                    p.exp += btl_exp
            if tmr == 5:
                if len(tmp_player) == 1: # player → tmp_player
                    set_message(str(btl_exp) + "ポイントの けいけんちを かくとく!")
tmp_player = [p for p in player if p.hp > 0]

生存していることを確認する
一行を追加しました。

あとは関係するリストを
playerからtmp_playerに
変更しています。

 

 

にげる

「にげる」について
1人目以外でにげた時に
次の戦闘の際に
にげたキャラから
コマンド選択するように
なっていたので
これを修正します。
※2人目でにげた場合、
 次の戦闘は2人目から
 コマンド選択になる

def main(screen, clock, font, fontS, player, area):
(省略)

        elif idx == 22: # 戦闘終了
            idx = 1
            monster.clear() # モンスターオブジェクトを削除
            dead_monster.clear()
            btl_exp = 0
            btl_player = 0 # 二人目以降で逃げた時に必要
            for p in player:
                p.spell_reset()
            return 1
btl_player = 0 # 二人目以降で逃げた時に必要

の一行を追加しただけです。

 

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

 

 

最後に

今回も仲間を追加したことに伴う
変更・修正を行いました。

他にも修正しないといけない箇所が
ありそうですが、
次回は戦闘から離れたいと思います。。

まとめサイトへ

 

 

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

 

 

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

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