CODE Python RPG

独学でPythonでRPGを作成する(第27回)攻撃呪文を使う

更新日:

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

前回は一つのみですが、回復呪文を使えるように変更しました。
今回は攻撃呪文を使えるように変更します。

前回、作成したメソッドは1つの呪文しか使えませんが、
色々な呪文が使える汎用的なメソッドにします。

目次

  1. 呪文シートを作成
  2. 呪文シートを読み込む
  3. spellメソッド
  4. 追加したメソッドに置き換え
  5. 呪文発動
  6. 最後に

呪文シートを作成

スプレッドシートの内容を変更します。

今まではキャラのシートに呪文の項目を入れていましたが
新しく呪文のシートを作成します。

変更前(キャラシート)

変更後(キャラシート)

M列はそのレベルになったら覚える呪文です。

 

新規(呪文シート)

呪文シートに新しく「上限」「下限」の項目を
追加しました。
呪文を使った時の回復するダメージ、与えるダメージを
下限〜上限の間でランダムな値にします。

前回も使ったrandintを使います。
randintの詳細はこちら
random.randint(下限, 上限)

 

スプレッドシートの変更は以上です。

 

 

呪文シートを読み込む

先ほど作成した呪文シートの値を読み込む為に
chara.pyを変更します。

ws_chara = wb.worksheet("キャラ")
ws_mon = wb.worksheet("モンスター")	ws_mon = wb.worksheet("モンスター")
ws_spell = wb.worksheet("呪文") # 追加
(省略)
# chara1_usemp = ws_chara.col_values(14) # 削除(消費MP)
# chara1_spell_target = ws_chara.col_values(15) # 削除(呪文のターゲット)
# chara1_spell_explain = ws_chara.col_values(16) # 削除(呪文の説明)
(省略)

(以下追加)
spell_name = ws_spell.col_values(1) # A列
spell_usemp = ws_spell.col_values(2) # B列 消費MP
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_usemp_dict = {}
spell_lo_dict = {}
spell_up_dict = {}
spell_explain_dict = {}
spell_target_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]))

class Chara():
(省略)

    def get_spell_usemp(self, str_spell): # 消費MPを返す
        return spell_usemp_dict[str_spell]
    def get_spell_explain(self, str_spell): # 呪文の説明を返す
        return spell_explain_dict[str_spell]
    def get_spell_target(self, str_spell): # ターゲットを返す
        return spell_target_dict[str_spell]

 

「以下追加」のところから変数を追加して
呪文のシートの値を取得しています。

次に辞書型の変数を用意しました。
辞書型の詳細はこちら

spell_usemp_dict = {}
spell_lo_dict = {}
spell_up_dict = {}
spell_explain_dict = {}
spell_target_dict = {}

spell_usemp_dict["ホイミ"] = 3
のようになるので、
使う呪文の消費MPを知りたい時などに使います。

辞書型の変数には
setdefaultを使って要素を追加します。

使い方は以下のような感じです。

spell_usemp_dict = {}
spell_usemp_dict.setdefault("ホイミ", 3)
print(spell_usemp_dict)

# 結果
# {'ホイミ': 3}

このsetdefaultを使ってfor文で
各変数に要素を追加しています。

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]))

1行目はシートの項目名の為、2行目から開始する為に
enumerate(spell_name[1:], 1)
としています。

enumerateの2つ目の引数を「1」にすると
「i」は1から始まります。
指定しない場合は0から始まります。
enumerateの詳細はこちら

あとは各変数を取得する為に
get_spell_usemp
get_spell_explain
get_spell_target
のメソッドを追加しました。

引数のstr_spellは使う呪文です。
ホイミの時は
spell_usemp_dict["ホイミ"] → 3
となり、消費MPの3を返します。

 

消費MP、ターゲット、呪文の説明は
今までレベルアップした時に取得していましたが
上記のメソッドで取得する為、
以下のように削除します。

class Brave(Chara):
(省略)
    def master_spell(self):
        if chara1_spell[self.lv] != "": # レベルアップして覚える呪文があるなら
            self.mas_spell.append(chara1_spell[self.lv])
            self.usemp.append(chara1_usemp[self.lv]) # 削除
            self.spell_explain.append(chara1_spell_explain[self.lv]) # 削除
            self.spell_target.append(chara1_spell_target[self.lv]) # 削除
            return chara1_spell[self.lv]
        return ""

 

 

spellメソッド

前回、呪文ホイミのメソッドであるhoimiを
作成しましたが、新しく追加した変数を使って
汎用的なメソッドに変更します。

変更前

class Chara():
(省略)

    def hoimi(self):
        if self.mp < 3:
            return -1 # MPが足りない
        else:
            self.mp -= 3 # 消費MP
            return random.randint(30, 40) # 30-40回復

変更後

class Chara():
(省略)
    def spell(self, str_spell):
        if self.mp < spell_usemp_dict[str_spell]:
            return -1 # MPが足りない
        else:
            self.mp -= spell_usemp_dict[str_spell] # 消費MP
            return random.randint(spell_lo_dict[str_spell], spell_up_dict[str_spell])

    def get_spell_usemp(self, str_spell):
        return spell_usemp_dict[str_spell]
    def get_spell_explain(self, str_spell):
        return spell_explain_dict[str_spell]
    def get_spell_target(self, str_spell):
        return spell_target_dict[str_spell]

 

hoimiメソッドでは定数を使った為
ホイミのみにしか使えなかったですが
新しく追加した変数に置き換えることで
他の呪文でも使えるように変更しました。

消費MP(spell_usemp_dict)
下限(spell_lo_dict)
上限(spell_up_dict)

引数のstr_spellは使う呪文です。
ホイミの時は
spell_usemp_dict["ホイミ"] → 3
spell_lo_dict["ホイミ"] → 30
spell_up_dict["ホイミ"] → 40
となります。

randintを使って、
呪文を使った時の回復するダメージ、与えるダメージを
下限〜上限の間でランダムな値にします。
random.randint(spell_lo_dict[str_spell], spell_up_dict[str_spell])

戻り値はターゲットが味方の時は回復する値、
ターゲットがモンスターの時は与えるダメージの値
となります。
※get_spell_targetメソッドの戻り値で判定します。
 戻り値が0 → ターゲットが味方
 戻り値が1 → ターゲットがモンスター

 

chara.pyの変更は以上です。

 

 

追加したメソッドに置き換え

battle.pyを変更します。

まず、新しく追加したメソッドに置き換えます。

def draw_battle(bg, fnt, obj, player):
(省略)

            else:
                # 呪文対象が敵の時、呪文対象を表示させない(点滅)
                if idx == 25 and i == spell_target_i and player.get_spell_target(player.mas_spell[sel_spell_i]) == 1 and tmr%5 == 0:
                    pass
                else:
                    bg.blit(mon.img, [monster[i].x, monster[i].y])
(省略)

    # 名前の表示(回復呪文の対象を選択中の時は点滅)
    if idx == 25 and player.get_spell_target(player.mas_spell[sel_spell_i]) == 0: # 呪文の対象の選択中,呪文の対象が味方なら
        draw_text(bg, "{}".format(player.name), x_i_t, y_i+6, fnt, BLINK[tmr%6])
(省略)

        # 呪文の説明
        tmp_explain = player.get_spell_explain(player.mas_spell[tmp_sel_spell_i]).split("\n")
(省略)

        # 呪文の消費MP/残りMP
        draw_text(bg, "{}/{}".format(player.get_spell_usemp(player.mas_spell[tmp_sel_spell_i]), player.maxmp), x_i_t, bg.get_height()*0.9+20, fnt, col)
(省略)

def spell_target_select(bg, key, player): # 呪文の対象を選択
    global idx, spell_target_i
    ent = False
    if player.get_spell_target(player.mas_spell[sel_spell_i]) == 0: # 呪文の対象が味方

ターゲット
変更前:player.spell_target[sel_spell_i]
変更後:player.get_spell_target(player.mas_spell[sel_spell_i])

呪文の説明
変更前:spell_explain[tmp_sel_spell_i]
変更後:get_spell_explain(player.mas_spell[tmp_sel_spell_i])

消費MP
変更前:player.usemp[tmp_sel_spell_i]
変更後:player.get_spell_usemp(player.mas_spell[tmp_sel_spell_i])

としています。

player.mas_spell[sel_spell_i]
は使う呪文です。
※mas_spell:覚えた呪文
 sel_spell_i:選択した呪文(添字)

 

新しく追加したメソッドの置き換えは以上です。

 

 

呪文発動

前回はホイミのみでしたが、
汎用的なメソッドに変更したspellメソッドを使って、
色々な呪文を発動します。

変更前

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

        elif idx == 26: # 呪文発動
            draw_battle(screen, fontS, turn_obj, player)
            if turn_obj.mas_spell[sel_spell_i] == "ホイミ":
                if tmr == 1:
                    set_message(turn_obj.name + " は ホイミを となえた!")
                    re_dmg = turn_obj.hoimi()
                if 2 <= tmr and tmr <= 4:
                    pygame.draw.rect(screen, BLINK[tmr%6], [0, 0, screen.get_width(), screen.get_height()])
                if tmr == 5:
                    if re_dmg != -1:
                        player.hp += re_dmg
                        if player.hp > player.maxhp:
                            player.hp = player.maxhp
                        set_message(player.name + " の キズが かいふくした!")
                    else:
                        set_message("しかし MPがたりない!")

                if tmr == 8:
                    init_message()
                    idx = 24 # ターンの確認
                    tmr = 0

 

変更後

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

        elif idx == 26: # 呪文発動
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                set_message(turn_obj.name + " は " + turn_obj.mas_spell[sel_spell_i] + " となえた!")
                dmg = turn_obj.spell(turn_obj.mas_spell[sel_spell_i])
            if 2 <= tmr and tmr <= 4:
                pygame.draw.rect(screen, BLINK[tmr%6], [0, 0, screen.get_width(), screen.get_height()])

            if turn_obj.get_spell_target(turn_obj.mas_spell[sel_spell_i]) == 0:
                if tmr == 5:
                    if dmg != -1:
                        player.hp += dmg
                        if player.hp > player.maxhp:
                            player.hp = player.maxhp
                        set_message(player.name + " の キズが かいふくした!")
                    else:
                        set_message("しかし MPがたりない!")

            if turn_obj.get_spell_target(turn_obj.mas_spell[sel_spell_i]) == 1:
                if tmr == 5:
                    if dmg != -1:
                        monster[spell_target_i].hp -= dmg
                        set_message(monster[btl_enemy].name + "に " + str(dmg)+"ポイントのダメージを与えた!")
                        if monster[spell_target_i].hp <= 0:
                            monster[spell_target_i].hp = 0
                            set_message(turn_obj.name + " は " + monster[spell_target_i].name + " をやっつけた!")
                            btl_exp += monster[spell_target_i].exp
                            del_battle_turn(monster[spell_target_i]) # ターンオブジェクトから消す(倒したモンスターは攻撃しない)
                            dead_monster.append(monster[spell_target_i]) # 先に加える
                            monster.pop(spell_target_i)
                    else:
                        set_message("しかし MPがたりない!")

                if not monster:
                    idx = 16 # 勝利
                    tmr = 0

            if tmr == 8:
                init_message()
                idx = 24 # ターンの確認
                tmr = 0

 

汎用的なメソッドにしたので
if turn_obj.mas_spell[sel_spell_i] == "ホイミ":
が不要になります。
※やはりここに全ての呪文のパターンは
 書けないですね(^_^;)

 

変更前
set_message(turn_obj.name + " は ホイミを となえた!")
re_dmg = turn_obj.hoimi()

変更後
set_message(turn_obj.name + " は " + turn_obj.mas_spell[sel_spell_i] + " となえた!")
dmg = turn_obj.spell(turn_obj.mas_spell[sel_spell_i])

上記のようにspellメソッドを使います。
戻り値は
ターゲットが味方:回復する値、
ターゲットがモンスター:与えるダメージの値

turn_obj.mas_spell[sel_spell_i]
は使う呪文です。

前回、追加したre_dmgは使わずに
回復も攻撃も値はdmgに入れます。

 

呪文のターゲットが味方の場合は
前回のホイミと同じです。

if turn_obj.get_spell_target(turn_obj.mas_spell[sel_spell_i]) == 0:
(回復する)

呪文のターゲットがモンスターの場合
モンスターにダメージを与えます。

dmgが-1の時はMPが足りない時です。

if turn_obj.get_spell_target(turn_obj.mas_spell[sel_spell_i]) == 1:
(モンスターにダメージを与える)

 

ダメージを与える動作は通常攻撃の時に似ています。
なので、同じ関数とかに出来るかもしれないですね(^_^)

        elif idx == 12: # プレイヤーの攻撃
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                set_message(turn_obj.name + " の攻撃!")
                dmg = 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
                if dmg > 0:
                    set_message(monster[btl_enemy].name + "に " + str(dmg)+"ポイントのダメージを与えた!")
                else:
                    set_message("ミス!" + monster[btl_enemy].name + "にダメージを与えられない!")
            if tmr == 11:
                monster[btl_enemy].hp = monster[btl_enemy].hp - dmg
                if monster[btl_enemy].hp <= 0:
                    monster[btl_enemy].hp = 0
                    set_message(turn_obj.name + " は " + monster[btl_enemy].name + " をやっつけた!")
                    btl_exp += monster[btl_enemy].exp
                    del_battle_turn(monster[btl_enemy]) # ターンオブジェクトから消す(倒したモンスターは攻撃しない)
                    dead_monster.append(monster[btl_enemy]) # 先に加える
                    monster.pop(btl_enemy)
                if not monster:
                    idx = 16 # 勝利
                    tmr = 0
            if tmr == 16:
                init_message()
                idx = 24 # ターンの確認
                tmr = 0

 

ご参考までに呪文を使っている様子を
動画にしました。
動画はこちらです。

 

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

 

 

最後に

今回はメソッドを汎用的にして
攻撃呪文を使えるように変更しました。

次回も引き続き呪文を変更していこうと思います。

まとめサイトへ

 

 

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

 

 

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

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