CODE Python RPG

独学でPythonでRPGを作成する(第39回)補助系の呪文を追加(4)

投稿日:

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

前回は補助系の呪文のヘナトスを追加しました。

今回も同じく補助系の呪文マヌーサを追加します。
あと会心の一撃や痛恨の一撃も追加します。

目次

  1. 呪文シート
  2. charaの変更
  3. battleの変更
  4. 最後に

呪文シート

「マヌーサ」は
相手を幻でつつみこみます。
※攻撃の命中率を下げます。

以下のページを参考にさせて頂きました。
呪文の効果はこちら
呪文の説明はこちら

説明のページでは
「敵全体」になっていますが、
効果のページの説明にあるように
「敵1グループ」(3)にしました。

また、マヌーサは相手に使う為
成功率を設けます。
成功率を75%(上限値を75)にします。
※下限は使いません。
※補助系の場合、上限・下限は
 成功率に使います。

 

シートは以上です。

 

 

charaの変更

最初に変数を追加します。

有効ターン数を格納する変数です。
回数を格納する変数は用意しません。
※何回、幻につつまれても
 命中率は変わりません。

class Chara():
    def __init__(self):
        self.x = 0 # プレイヤーのx座標, モンスター画像の表示位置
        self.y = 0 # プレイヤーのy座標, モンスター画像の表示位置

        self.use_defense = False # 防御を使っているか
        self.turn_sukara = 0 # スカラの有効ターン
        self.num_sukara = 0 # スカラを使った回数
        self.turn_rukani = 0 # ルカニの有効ターン
        self.num_rukani = 0 # ルカニが効いた回数
        self.turn_baikiruto = 0 # バイキルトの有効ターン
        self.turn_henatos = 0 # ヘナトスの有効ターン
        self.turn_manusa = 0 # マヌーサの有効ターン
        self.flag_manusa = False # マヌーサにかかっているか

マヌーサにかかっているかどうかは、
flag_manusa
を用意しなくても
turn_manusa
が0かどうかで判断できそうですが
何となく用意しました。

 

マヌーサ

マヌーサのメソッドを追加します。

class Chara():
(省略)

    def manusa(self, target): # マヌーサ
        if target.flag_manusa:
            target.turn_manusa = random.randint(4, 5) # 効果の残りターン
            return 1 # 既にマヌーサにかかっている
        else:
            if random.randint(0, 99) < spell_up_dict["マヌーサ"]: # 呪文の上限値が成功率
                target.flag_manusa = True
                target.turn_manusa = random.randint(4, 5) # 効果の残りターン
                return 2
            else: # 効かなかった
                return -1
    def invalid_manusa(self):
        self.turn_manusa = 0
        self.flag_manusa = False

既にマヌーサにかかっている時
有効ターンのみ再度確認します。
※繰り返しになりますが
 何回、幻につつまれても
 命中率は変わりません

if target.flag_manusa:
    target.turn_manusa = random.randint(4, 5) # 効果の残りターン
    return 1 # 既にマヌーサにかかっている

 

マヌーサにかかっていない時
成功率を下回っていると成功にしています。
※呪文シートの上限が75のため、
 75%成功する、25%失敗する。

else:
    if random.randint(0, 99) < spell_up_dict["マヌーサ"]: # 呪文の上限値が成功率
        target.flag_manusa = True
        target.turn_manusa = random.randint(4, 5) # 効果の残りターン
        return 2
    else: # 効かなかった
        return -1

 

最後のinvalid_manusaメソッドで
マヌーサの効果を無効にします。

spell_resetメソッドで呼び出します。

    def spell_reset(self):
(省略)
        self.invalid_manusa() # マヌーサの効果を消す

 

あと、前回のヘトナスについて
今回のマヌーサと同じように
失敗した時に-1を返すようにします。

あわせて、失敗した時を
elseにしました。
※特に理由はなく、何となくです。

変更前

    def henatos(self, target): # ヘナトス
        if random.randint(0, 99) > spell_up_dict["ヘナトス"]: # 呪文の上限値が成功率
            return 1 # 効かなかった
        tmp_atk = target.atk # 現在の攻撃力
        if target.atk >= target.get_atk(): # 元の攻撃力以上なら
            target.atk -= int(0.5 * target.get_atk()) # 元の攻撃力の半分をひく
        # 既に元の攻撃力より小さいなら変えない(どんなに小さくても半分まで)
        tmp_atk = target.atk - tmp_atk
        target.turn_henatos = random.randint(4, 5) # 効果の残りターン
        return tmp_atk # 変更後から変更前を引いた値

変更後

    def henatos(self, target): # ヘナトス
        if random.randint(0, 99) < spell_up_dict["ヘナトス"]: # 呪文の上限値が成功率
            tmp_atk = target.atk # 現在の攻撃力
            if target.atk >= target.get_atk(): # 元の攻撃力以上なら下げる、既に元の攻撃力より小さいなら変えない(どんなに小さくても半分まで)
                target.atk -= int(0.5 * target.get_atk()) # 元の攻撃力の半分をひく
            tmp_atk = tmp_atk - target.atk # 少なくなるので、元の値からひく
            target.turn_henatos = random.randint(4, 5) # 効果の残りターン
            return tmp_atk
        else: # 効かなかった
            return -1

 

 

呪文を使う

呪文を使う時のメソッドuse_spellを
変更します。

class Chara():
(省略)
 
   def use_spell(self, str_spell, target, spell_target_no):
(省略)
        elif str_spell == "ヘナトス":
            tmp_atk = self.henatos(target)
            if tmp_atk == -1:
                message = target.name + " には きかなかった!"
            elif tmp_atk > 0:
                message = target.name + " の こうげきりょくを " + str(tmp_atk) + "さげた!"
            elif tmp_atk == 0: # 効果はなくても残りターンは増える
                message = target.name + " には こうかが なかった!"
            # 増えることは無いので、elseは省略
            return -1, message

        elif str_spell == "マヌーサ":
            tmp = self.manusa(target)
            if tmp == -1:
                message = target.name + " には きかなかった!"
            elif tmp == 1:
                message = target.name + "は まぼろしに つつまれている!"
            elif tmp == 2:
                message = target.name + "は まぼろしに つつまれた!"
            return -1, message

        point = random.randint(spell_lo_dict[str_spell], spell_up_dict[str_spell])
        if spell_target_no == 0 or spell_target_no == 1: # ターゲット(0:味方単体,1:味方全員,2:敵単体,3:敵グループ,4:敵全体)
            message = target.name + " の キズが かいふくした!"
        else:
            message = target.name + "に " + str(point) + "ポイントのダメージを与えた!"
        return point, message

 

manusaメソッドを呼び出した後
戻り値によって、戦闘メッセージを
変えています。

また、先程ヘトナスの戻り値を
変更したので
一緒に変えました。

 

 

攻撃力の計算

攻撃力を計算する
attackメソッドを変更します。

前回まで戻り値は
ダメージのみでしたが
戦闘メッセージも
返すようにします。

    def attack(self, obj):
        message = []
        if self.flag_manusa: # 攻撃者がマヌーサにかかっている
            if random.random() > 3/8: # 命中率が37.5%
                message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
                return 0, message
        
        base_dmg = self.atk/2 - obj.dfs/4
        if base_dmg <= 0:
            base_dmg = 0
        width_dmg = base_dmg/16 + 1
        min_dmg = int(base_dmg - width_dmg) if base_dmg != 0 else 0
        max_dmg = int(base_dmg + width_dmg)
        dmg = random.randint(min_dmg, max_dmg)
        if dmg > 0:
            message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
        else:
            message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
        return dmg, message

 

マヌーサがかかっている時の
動作も追加しています。

if self.flag_manusa: # 攻撃者がマヌーサにかかっている
    if random.random() > 3/8: # 命中率が37.5%
        message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
        return 0, message

マヌーサにかかっている時の
命中率は37.5%にしています。
※62.5%は攻撃が当たらない

 

 

会心の一撃と痛恨の一撃

会心の一撃と痛恨の一撃の機能を
attackメソッドに追加します。

効果などは
それぞれ以下のページを
参考にさせて頂きました。
会心の一撃
痛恨の一撃

相手の守備力に関係なく
攻撃力とほぼ同じダメージを与えます。
※ダメージ幅を±5%にしています。

    def attack(self, obj):
        message = [] 
        if random.random() < 1/32:
            base_dmg = self.get_atk()
            min_dmg = int(base_dmg - 0.05*base_dmg)
            max_dmg = int(base_dmg + 0.05*base_dmg)
            dmg = random.randint(min_dmg, max_dmg)
            message.append("かいしんの いちげき!")
            message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
            return dmg, message
        
        if self.flag_manusa: # 攻撃者がマヌーサにかかっている
            if random.random() > 3/8: # 命中率が37.5%
                message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
                return 0, message
(省略)

 

会心の一撃はマヌーサとか
関係なく命中させる為
一番最初に持ってきました。

1/32の確率で発生します。

if random.random() < 1/32:
    base_dmg = self.get_atk()
    min_dmg = int(base_dmg - 0.05*base_dmg)
    max_dmg = int(base_dmg + 0.05*base_dmg)
    dmg = random.randint(min_dmg, max_dmg)
    message.append("かいしんの いちげき!")
    message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
    return dmg, message

 

会心の一撃はプレイヤー、
痛恨の一撃はモンスター
の攻撃とします。

どちらも機能は同じですが
戦闘メッセージが異なります。

ので、BraveクラスとMonsterクラスに
それぞれattackメソッドを追加しました。

class Chara():
(省略)
    # def attack(self, obj): # 削除
(省略)

class Brave(Chara):
(省略)

    def attack(self, obj):
        message = []
        if random.random() < 1/32:
            base_dmg = self.get_atk()
            min_dmg = int(base_dmg - 0.05*base_dmg)
            max_dmg = int(base_dmg + 0.05*base_dmg)
            dmg = random.randint(min_dmg, max_dmg)
            message.append("かいしんの いちげき!")
            message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
            return dmg, message
        
        if self.flag_manusa: # 攻撃者がマヌーサにかかっている
            if random.random() > 3/8: # 命中率が37.5%
                message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
                return 0, messag
        
        base_dmg = self.atk/2 - obj.dfs/4
        if base_dmg <= 0:
            base_dmg = 0
        width_dmg = base_dmg/16 + 1
        min_dmg = int(base_dmg - width_dmg) if base_dmg != 0 else 0
        max_dmg = int(base_dmg + width_dmg)
        dmg = random.randint(min_dmg, max_dmg)
        if dmg > 0:
            message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
        else:
            message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
        return dmg, message
(省略)

class Monster(Chara):
(省略)

    def attack(self, obj):
        message = []
        if random.random() < 1/32:
            base_dmg = self.atk
            min_dmg = int(self.atk - 0.05*self.atk)
            max_dmg = int(self.atk + 0.05*self.atk)
            dmg = random.randint(min_dmg, max_dmg)
            message.append("つうこんの いちげき!")
            message.append(obj.name + "は " + str(dmg) + "ポイントの ダメージを うけた!")
            return dmg, message

        if self.flag_manusa: # 攻撃者がマヌーサにかかっている
            print("マヌーサ")
            if random.random() > 3/8: # 命中率が37.5%
                message.append("ミス!" + obj.name + " は ダメージを うけない!")
                return 0, message
        
        base_dmg = self.atk/2 - obj.dfs/4
        if base_dmg <= 0:
            base_dmg = 0
        width_dmg = base_dmg/16 + 1
        min_dmg = int(base_dmg - width_dmg) if base_dmg != 0 else 0
        max_dmg = int(base_dmg + width_dmg)
        dmg = random.randint(min_dmg, max_dmg)
        if dmg > 0:
            message.append(obj.name + "は " + str(dmg) + "ポイントの ダメージを うけた!")
        else:
            message.append("ミス!" + obj.name + " は ダメージを うけない!")
        return dmg, message

※attackメソッドは
 Charaクラスのままで
 引数を一つ増やして、
 プレイヤーかモンスターの
 どちらの攻撃かを
 判断しても良いと思います

 

 

攻撃の回避

攻撃についてもう一つ
回避率を追加します。

前回まで、攻撃は確実に当たっていましたが
一定の確率で攻撃をかわすようにします。

回避について以下のページを
参考にさせて頂きました。
回避率

(省略)

    def attack(self, obj):
        message = []
        if random.random() < 1/32:
            base_dmg = self.get_atk()
            min_dmg = int(base_dmg - 0.05*base_dmg)
            max_dmg = int(base_dmg + 0.05*base_dmg)
            dmg = random.randint(min_dmg, max_dmg)
            message.append("かいしんの いちげき!")
            message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
            return dmg, message
        
        if self.flag_manusa: # 攻撃者がマヌーサにかかっている
            if random.random() > 3/8: # 命中率が37.5%
                message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
                return 0, message
        
        if obj.quick <= 400:
            avoid = 1/64 + obj.quick//80 * (1/192)
        elif obj.quick <= 500:
            avoid = 1/64 + 5 * (1/192) + (obj.quick-400)//25 * (1/32)
        else:
            avoid = 1/6
        if random.random() < avoid:
            dmg = 0
            if random.randint(0, 1) == 0:
                message.append(obj.name + "は ひらりと みをかわした!")
            else:
                message.append(obj.name + "は すばやく みをかわした!")
            return dmg, message
        
        base_dmg = self.atk/2 - obj.dfs/4
        if base_dmg <= 0:
            base_dmg = 0
        width_dmg = base_dmg/16 + 1
        min_dmg = int(base_dmg - width_dmg) if base_dmg != 0 else 0
        max_dmg = int(base_dmg + width_dmg)
        dmg = random.randint(min_dmg, max_dmg)
        if dmg > 0:
            message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
        else:
            message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
        return dmg, message

 

参考ページの以下を使っています。

「素早さ400未満では素早さが
 80上がるごとに回避率が1/192上がり、
 400以上では素早さが
 25上がるごとに回避率が1/32上がる。」

if obj.quick <= 400:
    # 80上がるごとに回避率が1/192上がり
    avoid = 1/64 + obj.quick//80 * (1/192)
elif obj.quick <= 500:
    # 25上がるごとに回避率が1/32上がる
    avoid = 1/64 + 5 * (1/192) + (obj.quick-400)//25 * (1/32)
else: # 500より上は1/6
    avoid = 1/6

※Braveクラス、Monsterクラスとも
 同じ機能を追加しています。

商の整数部分は「//」を使います。
詳細はこちら

素早さが50
 → obj.quick//80は0
素早さが83
 → obj.quick//80は1
素早さが350
 → obj.quick//80は4

 

ランダムで数値を取得して
その数値が回避率(avoid)より
小さい値となったら
攻撃を回避します。

if random.random() < avoid:
    dmg = 0

 

回避した時のメッセージは
2つにしました。
どちらのメッセージになるかは
ランダムです。

if random.randint(0, 1) == 0:
    message.append(obj.name + "は ひらりと みをかわした!")
else:
    message.append(obj.name + "は すばやく みをかわした!")

 

 

chara.pyは以上です。

 

 

battleの変更

battle.pyを変更します。

補助呪文の残りターンを確認する
check_spell_effect関数に
マヌーサを追加します。

def check_spell_effect(player):
(省略)

        if obj.turn_manusa != 0:
            obj.turn_manusa -= 1
            if obj.turn_manusa == 0:
                check_message.append(obj.name + " のマヌーサ効果が消えた!")
                obj.invalid_manusa() # マヌーサ効果を消す
    return check_message

 

attckメソッドで
戦闘メッセージを返すことにしたので
攻撃の時の機能を少し変えます。

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

        elif idx == 12: # プレイヤーの攻撃
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                set_message(turn_obj.name + " の攻撃!")
                dmg, get_message = player.attack(monster[btl_enemy])
            if 2 <= tmr and tmr <= 4:
                screen.blit(imgEffect[0], [monster[btl_enemy].x+monster[btl_enemy].img.get_width()-tmr*80, tmr*80])
            if tmr == 5:
                emy_blink = 5
                for mes in get_message:
                    set_message(mes)
            if tmr == 11:
                btl_exp += do_attack(btl_enemy, dmg, turn_obj)
                check_monster()
            if tmr == 16:
                init_message()
                idx = 24 # ターンの確認

attackメソッドから受けた
戦闘メッセージを
tmrが5の時に表示します。

if tmr == 5:
    emy_blink = 5
    for mes in get_message:
        set_message(mes)

 

モンスターの攻撃の時も
同様です。

        elif idx == 13: # モンスターの攻撃
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 5:
                set_message(turn_obj.name + " の攻撃!")
                emy_step = 30
            if tmr == 9:
                dmg, get_message = turn_obj.attack(player)
                for mes in get_message:
                        set_message(mes)
                if dmg > 0:
                    dmg_eff = 5
                else:
                    dmg_eff = 0
                emy_step = 0
            if tmr == 15:
                player.hp = player.hp - dmg
                if player.hp <= 0:
                    player.hp = 0
                    idx = 15 # 敗北
                    tmr = 0
            if tmr == 20:
                init_message()
                idx = 24 # ターンの確認

attackメソッドから受けた
戦闘メッセージを
tmrが9の時に表示します。

if tmr == 9:
    dmg, get_message = turn_obj.attack(player)
    for mes in get_message:
        set_message(mes)

 

攻撃に関する変更は以上です。

 

あと、少しだけ
勝利した時に
獲得した経験値を表示します。
※戦闘メッセージの
 1行を追加しました。

        elif idx == 16: # 勝利
(省略)
            if tmr == 5:
                set_message(player.name + " は " + str(btl_exp) + "ポイントの けいけんちを かくとく!") # 追加
                if player.exp >= player.lv_exp:
                    idx = 17
                    tmr = 0

あともう一つ
逃げた時にbtl_exp(経験値)を
0にしていましたが
戦闘が終わった時に
0にしているので
削除しました。

        elif idx == 14: # 逃げられる?
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                set_message(player.name + " は逃げ出した!")
            if tmr == 5:
                if btl_start == 1 or random.randint(0, 99) < escape_rate:
                    # btl_exp = 0 # 逃げたら0 ←戦闘が終わった時に0にしているので削除
                    idx = 22 # 戦闘終了

        elif idx == 22: # 戦闘終了
            idx = 1
            monster.clear() # モンスターオブジェクトを削除
            dead_monster.clear()
            btl_exp = 0 # ←0にしている
            player.spell_reset()
            return 1

idxが14の時の
btl_exp = 0
を消しました。

 

 

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

 

 

最後に

今回は相手に使う補助系の呪文マヌーサを追加しました。
また、会心の一撃や痛恨の一撃の機能、
攻撃の回避の機能を追加しました。

次回も・・何かします(^_^;)

まとめサイトへ

 

 

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

 

 

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

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