1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python初心者が簡単なRPG風テキストゲームを作ってみた結果 Part.2

Posted at

概要

 今回は以前作成したRPG風テキストゲームにストーリー性を追加したものになります。
しらかみゅさん(@shiracamus)が僕の書いたコードを、大幅にクラスやインスタンスの概念などを改良したものから、さらに私がストーリーを軽く肉付けしたものになります。
ストーリー自体は王道…、というかベタなものです。

コード内容

duel_braver.py
import time
import sys
import random
import keyboard
from typing import Callable

class Point:
    """ヒットポイント、マジックポイントなどいろいろステータス"""

    def __init__(self,point: int) -> None:
        self._remain: int = point
        self._max: int = point
    
    def __str__(self) -> str:
        """表示文字列を返す"""
        return str(self._remain)
    
    def use(self, point:int) -> bool:
        """point分使えるならTrueを返す、ポイント不足ならFalseを返す"""
        if self._remain < point:
            return False
        self.damage(point)
        return True
    
    def damage(self, point:int) -> None:
        """ダメージ分がHPから引かれる"""
        self._remain -= point

    def charge(self, point:int) -> None:
        """HP回復"""
        self._remain=min(self._remain+point,self._max)

    def is_full(self) -> bool:
        """HP上限突破を防ぐ"""
        return self._remain == self._max
    
    def is_empty(self) -> bool:
        """残りが0以下なら値を返す"""
        return self._remain <= 0
    
class Power:
    """武器攻撃力のクラス"""
    def __init__(self, minimum: int, maximum: int) -> None:
        self._points: range = range(minimum, maximum + 1)

    def use(self) -> int:
        """力を使う、ポイントを返す"""
        return random.choice(self._points)

class Magic:
    """魔法攻撃のクラス"""
    def __init__(self, name: str, point: int, power: Power) -> None:
        self._name: str = name # プレイヤー名
        self._point: int = point  # 魔法を使用するのに必要なポイント
        self._power: Power = power #攻撃

    def __str__(self) -> str:
        """表示文字列を返す"""
        return self._name

    def use(self, mp: Point) -> int:
        """mpを消費して魔法を使う。攻撃ポイントを返す"""
        if mp.use(self._point):
            return self._power.use()
        else:
            return 0  # mp不足
        
class HealthPosion:
    """回復薬"""

    def __init__(self,remain = 1,point = 100):
        self._remain = remain
        self._point = point

    def remain(self):
        return self._remain
    
    def use(self,hp: Point):
        """ポーションがあるならhpを回復してTrueを返す、ないならFalseを返す"""
        if self._remain <= 0:
            return False
        self._remain -= 1
        hp.charge(self._point)
        return True

class UpperPosion(HealthPosion):
    def __init__(self,remain = 1,point = 200):
        self._remain = remain
        self._point = point

    def remain(self):
        return self._remain
    
    def use(self,hp: Point):
        """ポーションがあるならhpを回復してTrueを返す、ないならFalseを返す"""
        if self._remain <= 0:
            return False
        self._remain -= 1
        hp.charge(self._point)
        return True

class Character:
    """プレイヤーと敵モンスターのデータの源!"""

    def __init__(self,name: str,health_point: int, magic_point: int,weapon:Power,shield:Power)->None:
        """キャラクターの雛型"""
        self._name: str = name
        self._hp:Point = Point(health_point)
        self._mp:Point = Point(magic_point)
        self._weapon:Power = weapon
        self._shield:Power = shield

    def __str__(self)->str:
        """表示文字列を返す"""
        return str(self._name)
    
    def _message(self,text: str,wait: int=1)->None:
        """キャラクタのメッセージ出力"""
        print(f"{self}:{text}")
        time.sleep(wait)

    def status(self)->str:
        """ステータスを返す"""
        return f"{self}のHP:{self._hp},MP:{self._mp}"
    
    def defense(self,point: int)->int:
        """攻撃を防御する"""
        return self._damage(point//2-self._shield.use()//4)
    
    def _damage(self,point)->int:
        """ダメージ処理"""
        self._hp.damage(point)
        self._message(f"{point}のダメージ")
        return point
    
    def is_dead(self)->bool:
        """死亡していたらTrueを返す"""
        return self._hp.is_empty()

class Weapon(Power):
    def __init__(self,name,minimum,maximum):
        self._name=name
        super().__init__(minimum,maximum)

    def __str__(self):
        """表示文字列を返す(武器名)"""
        return self._name

#プレイヤーのクラス    
class Player(Character):

    def __init__(self,name: str,weapon)->None:
        super().__init__(name,300,30,Power(0,0),Power(40,45))
        self._weapon:Weapon=weapon
        self._message(f'{weapon}で行く!')
        self._is_guard: bool=False
        self._health_posion: HealthPosion=HealthPosion(5)
        self._upper_posion:UpperPosion=UpperPosion(2)
        self._COMMANDS: dict[str,Callable[[Character],bool]]={
            'a':self._use_weapon,
            'm':self._use_magic,
            's':self._use_strong_magic,
            'g':self._guard,
            'd':self._heal,
            'h':self._upper_heal,
        }

    def status(self)->str:
        return f"{super().status()},回復薬:{self._health_posion.remain()},回復薬(上):{self._upper_posion.remain()}"
    
    #防御
    def defense(self,point: int)->int:
        """防御する"""
        if self._is_guard:
            self._is_guard = False
            if random.random() >= 0.1:
                self._message("攻撃を弾いた!")
                return 0
        return super().defense(point)
    
    def attack(self,monster: Character)->None:
        """攻撃する"""
        print("-------------------------")
        print("|a:攻撃 |m:攻撃魔法  |")
        print("|g:防御 |s:強力攻撃魔法|")
        print("|d:回復薬|h:回復薬(上)|")
        print("-------------------------")
        while((command := input("選択>>>"))not in self._COMMANDS or not self._COMMANDS[command](monster)):
            pass

    def _use_weapon(self,monster: Character)->bool:
        """武器を使って攻撃する"""
        self._message(f"「このォ!」")
        monster.defense(self._weapon.use())
        return True
    
    def _use_magic(self,monster: Character,magic: Magic = Magic("サンダーシュート",5,Power(125,130)))->bool:
        """魔法を使って攻撃する"""
        point = magic.use(self._mp)
        if point == 0:
            self._message(f"「MPが足りない!」")
            return False  # 再入力
        self._message(f"{magic}!」", 0)
        monster.defense(point)
        return True

    def _use_strong_magic(self,monster:Character, magic:Magic = Magic("フレイムバースト", 10, Power(145, 150))) -> bool:
        """強力魔法を使って攻撃する"""
        return self._use_magic(monster, magic)
    
    def _guard(self,monster) -> bool:
        """盾を使って防御する"""
        self._is_guard = True
        self._message(f"「盾で防ぐぞ!」")
        return True

    def _heal(self,monster:Character) -> bool:
        """生命力を回復する"""
        if self._hp.is_full():
            self._message(f"「今はいらないな」")
            return False #再入力
        if self._health_posion.use(self._hp):
            self._message(f"「力がみなぎって来たぞ!」\nライフが回復した!")
            return True
        else:
            self._message(f"「回復薬が無い!」")
            return False #再入力

    def _upper_heal(self,monster:Character) -> bool:
        if self._hp.is_full():
            self._message(f"「今は使わなくていいな」")
            return False
        if self._upper_posion.use(self._hp):
            self._message(f"「うおおおぉっ!!」")
            return True
        else:
            self._message(f"「まずい! 切れたか!」")
            return False

#モンスターのクラス
class Monster(Character):

    def __init__(self, name: str, hp: int, mp: int, ap: Power, sp: int, magic: Magic) -> None:
        super().__init__(name, hp, mp, ap, Power(sp, sp))
        self._magic: Magic = magic

    def attack(self, player: Character) -> None:
        if random.random() > 0.3:
            point = self._weapon.use()
            self._message(f"「喰らえ!」", 0)
        else:
            point = self._magic.use(self._mp)
            if point == 0:
                self._message("「MPが足りない!」")
                return
            self._message(f"{self._magic}!」", 0)
        if player.defense(point) == 0:
            self._message("「やるな」")
        else:
            self._message("「馬鹿め!」")

#モンスターの一覧   
MONSTERS = (
    Monster("スカルヘッド",250,50,Power(50,60),20,
        Magic("スカルヘッドバット",5,Power(50,60))),
    Monster("ゴーストナイト",300,70,Power(75,85),20,
        Magic("ダークスラッシュ",5,Power(100,105))),
    Monster("デスマシーン",310,75,Power(80,85),25,
        Magic("デスクラッシュ",5,Power(105,110))),
    Monster("ギルドラゴン",320,80,Power(85,95),25,
        Magic("バーンフレイム",5,Power(110,115))),
    Monster("ダークウィザー",350,90,Power(105,115),25,
        Magic("キルストーム",5,Power(120,125))),
    Monster("ネビュラシーザー",500,100,Power(130,135),30,
        Magic("ヘル・デストロイ",5,Power(170,175))), 
)

WEAPONS = {
    "m": Weapon("マスターブレード",120,130),
    "b": Weapon("ビッグソード",100,110),
    "s": Weapon("スマートダガー",80,90)
}

class Old_Man(Character):
    def __init__(self, name: str)->None:
        self._name=name
    
    def go_fight(self)->None:
        self._message(f"{name}よ、戦うのじゃ」")

    def help_me(self)->None:
        self._message(f"「大変だ! ワシ達の村に魔物が襲ってきた!\n      済まない、どうか助けてくれないか?」")

    def question(self) -> None:
        print()
        print("-------------------------")
        print("|yキー:はい,nキー:いいえ|")
        print("-------------------------")
        print()
        say=input('どうしますか?>>>')
        if say == 'y':
            self._message(f'「おお、やってくれるか。名前は何という?」')
        elif say == 'n':
            self._message(f"「そうか。仕方あるまい。\n     なら今の状況を受け入れるか」")
            game_over()
        else:
            self._message(f'「何をぐずぐずしている」')

def show_title() -> None:
    """タイトル表示"""
    print("*************")
    print("*DUEL BLAVER*")
    print("*************")

def go() -> Old_Man:
    old_man=Old_Man("長老ソル")
    old_man.go_fight()

def tell() -> Old_Man:
    old_man=Old_Man("長老ソル")
    old_man.question()

def entry_player() -> Player:
    global name
    """プレイヤー登場"""
    tell()
    name = input("名前を入力してください>>>")
    print()
    weapon = select_weapon()
    print()
    go()
    return Player(name,weapon)

def select_weapon() -> Weapon:
    print("----------------------------------------")
    print("|m:マスターブレード(入門者向け)    |")
    print("|b:ビッグソード  (プレイ済みの方向け)|")
    print("|s:スマートダガー (熟練者向け)    |")
    print("----------------------------------------")
    while(selection := input("武器を選択してください>>>")) not in WEAPONS:
        pass
    weapon = WEAPONS[selection]
    return weapon

def opening() -> None:
    show_title()
    time.sleep(1)
    print("一匹の魔物が王国を攻撃しようとしているぞ!")
    print("果たして止める事は出来るのか!?")
    time.sleep(2)
    old_man=Old_Man("長老ソル")
    old_man.help_me()
    time.sleep(1)

def ending() -> None:
    print("モンスターを倒した!\n")
    print(f"化け物は{name}の手で倒された。")
    print(f"この国にも平和が戻った。\nありがとう、{name}")
    time.sleep(1)
    print("************")
    print("*GAME CLEAR*")
    print("************")

def appear_monster() -> Monster:
    """モンスター出現"""
    monster = random.choice(MONSTERS)
    print(f"{monster}出現!")
    return monster

def game_over():
    print("一匹の魔物の手によってこの国は滅び、\n次第にそこは魔物の巣窟と化し、魔界帝国が誕生した。\n")
    time.sleep(2)
    print("***********")
    print("*GAME OVER*")
    print("***********")
    sys.exit()

def main() -> None:
    """主なゲームの流れ一式"""
    print();opening()
    player = entry_player()
    print()
    monster = appear_monster()
    time.sleep(1)

    while True:
        print()
        print(player.status());print(monster.status())
        time.sleep(2)

        print(f"\n***{player}のターン!***")
        player.attack(monster)
        if monster.is_dead():
            ending()
            return

        print(f"\n***{monster}のターン!***")
        monster.attack(player)
        if player.is_dead():
            print(f"{player}は敗れた・・・\n")
            game_over()
            return

if __name__ == "__main__":
    main()

感想

まだ長老のセリフ云々は改良出来ると思うので、今後もいろいろ手を加えていきたいです。
可能であれば、モンスターと連戦できるように改良もしたいところ。

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?