CODE Python RPG

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

投稿日:

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

前回は補助系の呪文のルカニを追加していきました。

今回も同じく補助系の呪文のバイキルトを追加します。
あと少し細かいところを変えます。

目次

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

呪文シート

「バイキルト」は
味方の攻撃力を上げます。

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

バイキルトは味方に使う為
成功率は100%にするので
上限・下限は使いません。
※補助系の場合、上限・下限は
 成功率に使います。

 

シートは以上です。

 

 

charaの変更

最初に変数の値を取得したり
宣言する場所を変えます。

呪文シートの情報の取得を
Charaクラスの外に出します。

特に意味はありません・・
何となくです(^_^;)

spell_name = ws_spell.col_values(1) # A列
spell_usemp = ws_spell.col_values(2) # B列
spell_lo = ws_spell.col_values(3) # C列
spell_up = ws_spell.col_values(4) # D列
spell_explain = ws_spell.col_values(5) # E列 呪文の説明
spell_target = ws_spell.col_values(6) # F列
spell_blink = ws_spell.col_values(7) # G列 呪文のいろ

spell_usemp_dict = {}
spell_lo_dict = {}
spell_up_dict = {}
spell_explain_dict = {}
spell_target_dict = {}
spell_blink_dict = {}
for i, tmp in enumerate(spell_name[1:], 1): # 呪文の性能を辞書型で取得
    spell_usemp_dict.setdefault(tmp, int(spell_usemp[i]))
    spell_lo_dict.setdefault(tmp, int(spell_lo[i]))
    spell_up_dict.setdefault(tmp, int(spell_up[i]))
    spell_explain_dict.setdefault(tmp, spell_explain[i])
    spell_target_dict.setdefault(tmp, int(spell_target[i]))
    spell_blink_dict.setdefault(tmp, spell_blink[i])

class Chara():
(省略)

# Charaクラスの外
def get_spell_usemp(str_spell):
    return spell_usemp_dict[str_spell]
def get_spell_explain(str_spell):
    return spell_explain_dict[str_spell]
def get_spell_target(str_spell):
    return spell_target_dict[str_spell]
def get_spell_blink(str_spell):
    return spell_blink_dict[str_spell]
    
def get_mon_area_list():
    return mon_area

変更前、↓はCharaクラスでしたが
外に出しました。

for i, tmp in enumerate(spell_name[1:], 1): # 呪文の性能を辞書型で取得
    spell_usemp_dict.setdefault(tmp, int(spell_usemp[i]))
    spell_lo_dict.setdefault(tmp, int(spell_lo[i]))
    spell_up_dict.setdefault(tmp, int(spell_up[i]))
    spell_explain_dict.setdefault(tmp, spell_explain[i])
    spell_target_dict.setdefault(tmp, int(spell_target[i]))
    spell_blink_dict.setdefault(tmp, spell_blink[i])
def get_spell_usemp(str_spell):
    return spell_usemp_dict[str_spell]
def get_spell_explain(str_spell):
    return spell_explain_dict[str_spell]
def get_spell_target(str_spell):
    return spell_target_dict[str_spell]
def get_spell_blink(str_spell):
    return spell_blink_dict[str_spell]

 

また、Charaクラスの
initで変数を定義します。

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.num_baikiruto = 0 # バイキルトが効いた回数
(省略)

class Brave(Chara):
    def __init__(self):
        super(Brave, self).__init__()
(省略)

class Monster(Chara):
    def __init__(self, num):
        super(Monster, self).__init__()

今までBraveクラスで
定義していた変数です。
※Braveクラスからは
 上記変数は削除します。

Braveクラスのinitで
super(Brave, self).__init__()
とすることで
Charaクラスのinitを呼び出します。

ので、Braveクラスで
use_defenseなどの変数が
使えます。

Monsterクラスでも同様です。
※モンスターも補助系の呪文を
 使えるようにします。
 そのうち、たぶん・・・

 

あと、バイキルトと関係ありませんが
ついでにattackメソッドを変更します。

変更前

class Chara():
(省略)

    def attack(self, obj):
        base_dmg = self.atk/2 - obj.dfs/4
        if base_dmg <= 0:
            return 0
        width_dmg = base_dmg/16 + 1
        min_dmg = int(base_dmg - width_dmg)
        max_dmg = int(base_dmg + width_dmg)
        return random.randint(min_dmg, max_dmg)

変更後

class Chara():
(省略)

    def attack(self, obj):
        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)
        return random.randint(min_dmg, max_dmg)

攻撃した時のダメージ計算です。

変更後は
攻撃対象の守備力が異常に高い場合でも
1ダメージを与えることもあり得ます。
※メタル系のモンスターをイメージしました。
※逆に弱いモンスターから
 1ダメージを受ける可能性もあります。

変更前
守備力が高い(ダメージが0以下)場合
無条件で与えるダメージは0

if base_dmg <= 0:
    return 0

 

変更後
守備力が高い(ダメージが0以下)場合
与えるダメージは0もしくは1

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)
return random.randint(min_dmg, max_dmg)

 

base_dmgが0の場合
min_dmg:0
max_dmg:1
random.randint(min_dmg, max_dmg)
 →0 もしくは 1

ちなみに
min_dmg = int(base_dmg - width_dmg) if base_dmg != 0 else 0
は三項演算子で、以下と同じです。

if base_dmg != 0:
    min_dmg = int(base_dmg - width_dmg)
else:
    min_dmg = 0

 

 

攻撃力の計算

スカラやルカニの時に
作成したcheck_dfsメソッドと
同じように攻撃力を計算する
check_atkメソッドを作成します。

    def check_dfs(self): # 防御力計算
(省略)

    def check_atk(self): # 攻撃力計算
        tmp_atk = self.atk # 現在の攻撃力
        self.atk = self.get_atk() # 元に戻す
        rate = self.num_baikiruto*2 # 何倍にするか確認(攻撃力を下げる呪文の回数はここでひく) バイキルトは2倍
        if rate >= 2:
            self.atk += self.get_atk() # 元々の攻撃力を足す(2倍)
        elif rate == 1:
            self.atk += int(0.5 * self.get_atk()) # 元々の攻撃力の半分を足す(1.5倍)
        elif rate == 0:
            self.atk = self.get_atk() # 変わらない
        elif rate <= -1:
            self.atk -= int(0.5 * self.get_atk()) # 元々の攻撃力の半分を引く(0.5倍)
        tmp_atk = self.atk - tmp_atk
        return tmp_atk # 変更後から変更前を引いた値
(省略)

class Brave(Chara):
(省略)

    def get_atk(self):
        return int(chara1_atk[self.lv])

 

まず、元の攻撃力を取得する
get_atkメソッドを
Braveクラスに作成します。

そして、check_dfsメソッドと同様に
check_atkメソッドで
最初に元の攻撃力に戻して
バイキルトを使った回数に
応じて攻撃力を変更します。

rate = self.num_baikiruto*2

が、コメントに記載した
攻撃力を下げる呪文は
また今度・・(^_^;)

なので、今の時点では
rateが1や-1以下になることは
ありません。

また、バイキルトは何回使っても
元の攻撃力の2倍にします。
※スカラのように999まで
 増加しません。

if rate >= 2:
    self.atk += self.get_atk() # 元々の攻撃力を足す(2倍)

 

攻撃力の計算は以上です。

 

 

バイキルトの追加

バイキルトを使った時の
baikirutoメソッドと
バイキルト効果を消す
invalid_baikirutoメソッドを
追加します。

class Chara():
(省略)

    def baikiruto(self, target): # バイキルト
        target.num_baikiruto += 1 # バイキルト継続中に使った回数
        target.turn_baikiruto = random.randint(4, 5) # 効果の残りターン
        tmp = target.check_atk()
        return tmp
    def invalid_baikiruto(self):
        self.turn_baikiruto = 0
        self.num_baikiruto = 0

 

味方に使うので成功率は100%です。
※やっていることはスカラと同じです。

 

 

呪文を使う

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

class Chara():
(省略)

    def use_spell(self, str_spell, target, spell_target_no): # デフォルト値を取らない引数がデフォルト値を取る引数の後になっているとエラーになる
        # 戻り値が-1は状態変化の呪文
        if str_spell == "スカラ":
(省略)

        elif str_spell == "バイキルト":
            tmp_atk = self.baikiruto(target)
            if tmp_atk > 0:
                message = target.name + " の こうげきりょくが " + str(tmp_atk) + "ふえた!"
            elif tmp_atk == 0:
                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
(省略)

    def spell_reset(self):
        self.invalid_sukara()
        self.invalid_rukani()
        self.invalid_baikiruto()

 

使った呪文がバイキルトの場合を
追加しました。

baikirutoメソッドで
攻撃力を確認して
その結果によって
戦闘メッセージを返すのみです。

 

あと、呪文の効果を消すメソッドを
まとめたspell_resetメソッドを
作成しました。

戦闘が終わった時など
spell_resetメソッドを呼び出して
全ての効果をまとめて消します。

 

chara.pyは以上です。

 

 

battleの変更

battle.pyを変更します。

まず、chara.pyで4つのメソッドを
Charaクラスの外に出したので
該当メソッドを使うときは
「chara.メソッド」に変更します。

def draw_battle(bg, fnt, obj, player):
(省略)
                if idx == 25:
                    if chara.get_spell_target(player.mas_spell[sel_spell_i]) == 2: # 呪文の対象がモンスター単体

変更前

if player.get_spell_target(player.mas_spell[sel_spell_i]) == 2:

 

変更後

if chara.get_spell_target(player.mas_spell[sel_spell_i]) == 2:

 

他にも何箇所かありますが
全て変更します。
※VSCodeですと
 こちらのショートカットが便利です。

 

次に補助系の呪文効果が
残っているかを確認する
関数を作ります。

前回までmainメソッドの
elif idx == 24:
で確認していたのを
関数にしただけです。

(省略)
def check_spell_effect(player):
    list_tmp = []
    list_tmp.extend([player])
    list_tmp.extend(monster)
    check_message = []
    for obj in list_tmp:
        if obj.turn_sukara != 0:
            obj.turn_sukara -= 1
            if obj.turn_sukara == 0:
                check_message.append(obj.name + " のスカラ効果が消えた!")
                obj.invalid_sukara() # スカラ効果を消す
                obj.check_dfs()
        if obj.turn_rukani != 0:
            obj.turn_rukani -= 1
            if obj.turn_rukani == 0:
                check_message.append(obj.name + " のルカニ効果が消えた!")
                obj.invalid_rukani() # ルカニ効果を消す
                obj.check_dfs()
        if obj.turn_baikiruto != 0:
            obj.turn_baikiruto -= 1
            if obj.turn_baikiruto == 0:
                check_message.append(obj.name + " のバイキルト効果が消えた!")
                obj.invalid_baikiruto() # バイキルト効果を消す
                obj.check_atk()
    return check_message

 

list_tmpにモンスターも入れており
戦闘に参加しているキャラ全員を
確認しています。
※extendの詳細はこちら

今は味方は一人ですが、
仲間を追加した場合も
ここで全員確認します。

なので、戦闘のターン内で
全員の行動が終わった時に
呪文効果の残ターン数を
まとめて確認するようにします。

この辺りの違いについて
ご参考までに動画にしました。
こちら

 

変更前

def get_battle_turn(player):
    global battle_order
    if not battle_order: # 全員行動したらプレイヤーのコマンド選択
        return None, 11 # コマンド入力待ちへ
(省略)

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

        elif idx == 24: # ターン確認
            tmr = 0
            turn_obj, idx = get_battle_turn(player)
            if turn_obj is not None:
(省略 ※呪文効果の確認)

変更後

def get_battle_turn(player):
    global battle_order
    if not battle_order: # 全員行動したらプレイヤーのコマンド選択
        return None, 27 # 呪文効果の確認へ
(省略)

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

        elif idx == 24: # ターン確認
            tmr = 0 # 0にしないと idx=12では battle_calに行かない()(ここに来た時点で+1されているので、ここに来る前に0にしても意味がない)
            turn_obj, idx = get_battle_turn(player)
(省略)

        elif idx == 27:
            check_message = check_spell_effect(player)
            for mes in check_message:
                set_message(mes)
                draw_battle(screen, fontS, turn_obj, player)
                pygame.display.update()
                time.sleep(0.8)
            init_message()
            idx = 11 # コマンド入力待ちへ

 

get_battle_turn関数で
全員の行動が終わった後
idx27に行くように変更しています。

idx27でcheck_spell_effect関数で
全員分の呪文効果の残ターンの確認と
戦闘メッセージを取得、
そして、それを表示しています。
※メッセージで表示されるのは
 呪文効果が消えたキャラのみです。

全員分を表示したら、
コマンド入力(idx11)へと移ります。

 

最後に
idx11で攻撃力の確認をして
idx27でspell_resetを呼び出して
呪文効果を消します。

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

        elif idx == 11: # プレイヤーのターン(入力待ち)
            btl_enemy = 0
            player.use_defense = False # ぼうぎょを元に戻す(ぼうぎょ効果を消す)
            player.check_dfs()
            player.check_atk() # 追加
(省略)

        elif idx == 22: # 戦闘終了
            idx = 1
            monster.clear() # モンスターオブジェクトを削除
            dead_monster.clear()
            btl_exp = 0
            player.spell_reset() # 呪文効果を消す
            return 1

 

バイキルトに関する変更は以上ですが
もう一つ、以下を変更しました。

「ぼうぎょ」するのは
playerのみとは限らないからです。
※今はplayerのみですけど・・(^_^;)

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

        elif idx == 18: # ぼうぎょ
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                set_message(turn_obj.name + "は みをまもっている。") # player → turn_objに変更
            if tmr == 5:
                init_message()
                idx = 24 # ターンの確認

 

 

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

 

 

最後に

今回は味方に使う補助系の呪文バイキルトを追加しました。

次回も補助系呪文に手をつけていきたいと思います。

まとめサイトへ

 

 

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

 

 

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

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