冒险者游戏
1、总体框架
角色扮演、RPG、回合制游戏、文字版、无界面。
说明文档内容附在文末
1.1 实体关系图 E-R图
要组成一个大整体,首先要有各种实体,这在大学里面叫什么分析来着,ER图?反正就是那么个东西,工欲善其事必先利其器,先分析就对了,只有结构清晰了,才能下笔如有神。
1.2 活动流程图
2 具体模块:
每个实体写成一个类,这叫封装,面向对象的过程中是一个非常好的习惯。后续添加属性或者方法都很容易定位。
2.1 实体bean类
(1) 冒险者adventurer
E-R图
代码实现
从ER图可以看到冒险者拥有几种属性:名字name,最大血量maxHP,当前血量hp,是否存活alive,是否眩晕stunned,是否处于格挡状态blocking。
同时,冒险者可以equip装备:武器,盾牌,护甲。
同时,冒险者还具有物品栏。
将这些属性写在init里,需要用户初始化的东西通过传参的形式传递用户需求。无须用户设计的就直接写在代码里面即可,就不用通过传参的方式了,防止过于冗余。
class Adventurer:
# 名字name,最大血量maxHP,当前血量hp,生存状态alive,格挡blocking,控制stunned,
def __init__(self, name, weapon, shield, armour, inventory, maxHP=100):
self.name = name
self.maxHP = maxHP
self.hp = maxHP
self.alive = 'true'
self.stunned = 'false'
self.blocking = 'false'
# 初始化装备
self.weapon = weapon # 初始化武器设置
self.shield = shield
self.armour = armour
# 初始化物品栏
self.inventory = inventory
# showHero:输出冒险者的面板
def showHero(self):
print('名字:', self.name)
self.showStatus()
self.showEquipment()
self.showInventory()
def isAlive(self):
if self.hp <= 0:
self.alive = 'false'
return "false"
else:
self.alive = 'true'
return "true"
def showStatus(self):
print('状态:')
print('最大生命值:', self.maxHP, '\t当前血量:', self.hp, '\talive:', self.alive)
print('stunned:', self.stunned, '\tblocking:', self.blocking)
def showEquipment(self):
print('装备:')
print(f"weapon:\tname={
self.weapon.name};\tdamage={
self.weapon.damage};\t\tweight={
self.weapon.weight};")
print(
f"shield:\tname={
self.shield.name};\tarmourRating={
self.shield.armourRating};\tweight={
self.shield.weight};\tpercentage={
self.shield.percentage}")
print(f"armour:\tname={
self.armour.name};\tarmourRating={
self.armour.armourRating};\tweight={
self.armour.weight}")
def showInventory(self):
print('物品栏:')
num = len(self.inventory.items)
if num == 0:
print("空的。")
else:
for i in range(num):
item = self.inventory.items[i]
print(f"item{
i + 1}:\t{
item.name}")
def getWeight(self):
return self.weapon.weight+self.armour.weight+self.shield.weight
根据后面的需求,我们应该能够展示冒险者的信息,所以构造一个showhero()方法,本来我是写在一起的,但是,后面也有分开展示的需求,如果每次都全部展示就太过于冗余啦,所以我把每个showhero分成三个部分:show状态,show装备,show物品栏。分别通过三个方法实现。
isAlive判断存货状态和getweight计算身上装备的重量。这些都是后来功能需要又添加的,这就体现出来把bean分离出单个文档的好处了,
(2) 背包Inventory
代码实现
原需求的背包是要一个6格的数组放置,但我不喜欢,这样不利于我以后的扩充,且在代码上不美观,感觉不够高级,所以我选择了可变数组作为背包属性。没有设置类或者结构体,直接用两个属性来表示,一个表示空位,一个表示元素。
背包理应具有两个方法:放进去物品,丢掉物品,展示背包栏。
放物品要考虑放满的情况,说明书要求置换,但我觉得先丢后放会更简单,更适合我这种结构。逻辑清晰,看代码就行了。
class Inventory:
def __init__(self):
self.empty = 6
self.items = []
def getItem(self, item):
if self.empty == 0:
print("背包放满了,不能再放了,你需要从以下物品中输入序号(-1表示不替换)替换一件物品:")
self.showBag()
while 1:
i = int(input("想要扔掉物品的序号(-1返回上一级表示你不换了):"))
if i == "-1": # 上一级
break
else:
ack = input("你确认要换这件物品?(Y or N)")
if ack == "Y":
self.dropItem(i-1)
else:
print("已取消更换,请重新选择")
continue
self.items.append(item)
self.empty -= 1
def dropItem(self, i):
self.items.pop(i - 1)
self.empty += 1
def showBag(self):
for i in range(len(self.items)):
item = self.items[i]
print(f"item{
i + 1}:{
item.name}")
(3) 物品Item
代码实现
物品分为两种,药水和装备,一开始对python中的类不熟悉,考虑分开去做,但是最后都是要放在物品栏里面,所以没有好的想法,还是需要是一个类别,所以敲定必须是类,就直接搜了类继承相关的知识,通过继承实现,确实好用。
class Item:
def __init__(self, name):
self.name = name
① 药水potion
代码实现
继承物品类item,根据需求,一个名字就够了,通过名字判断功用。
class Potions(Item):
# 创建药水类
def __init__(self, name):
self.name = name
② 装备equipments
武器E-R图
盾牌E-R图
护甲E-R图
继承物品类Item,根据需求,武器有三个属性,盾牌四个属性,护甲三个属性,设置就完事了。
class Equipments(Item):
# 创建武器类
class Weapon:
def __init__(self, name, damage, weight):
self.name = name
self.damage = damage
self.weight = weight
# 创建盾牌类
class Shield:
def __init__(self, name, armourRating, percentage, weight):
self.name = name
self.armourRating = armourRating
self.percentage = percentage
self.weight = weight
# 创建盔甲类
class Armour:
def __init__(self, name, armourRating, weight):
self.name = name
self.armourRating = armourRating
self.weight = weight
附加文档也提供了一些装备的属性作为参考:
2.2 活动方法
(1) 初始化参数 init
① 初始化物品列表 getItemList()
从附加文档的参考装备中挑选一部分作为系统物品库,放入ItemList列表中存待用。
def getItemsList():
w1 = Equipments.Weapon('Redsword', 15, 1)
s1 = Equipments.Shield('Redshield', 10, 1.5, 0.1)
a1 = Equipments.Armour('Redarmour', 10, 2)
w2 = Equipments.Weapon('Orangesword', 15, 1)
s2 = Equipments.Shield('Orangeshield', 15, 2, 0.15)
a2 = Equipments.Armour('Orangearmour', 15, 2.5)
w3 = Equipments.Weapon('Yellowsword', 20, 1.5)
s3 = Equipments.Shield('Yellowshield', 20, 2.5, 0.2)
a3 = Equipments.Armour('Yellowarmour', 10, 2)
w4 = Equipments.Weapon('Greensword', 40, 2.5)
s4 = Equipments.Shield('Greenshield', 30, 3, 0.25)
a4 = Equipments.Armour('Greenarmour', 30, 3.5)
w5 = Equipments.Weapon('Cyansword', 50, 3)
s5 = Equipments.Shield('Cyanshield', 40, 3.5, 0.3)
a5 = Equipments.Armour('Cyanarmour', 40, 4)
w6 = Equipments.Weapon('Bluesword', 60, 3.5)
s6 = Equipments.Shield('Blueshield', 50, 4, 0.35)
a6 = Equipments.Armour('Bluearmour', 50, 4.5)
potion1 = Potions("healthPotion")
potion2 = Potions("damagePotion")
itemsList = [w1, w2, w3, w4, w5, w6, s1, s2, s3, s4, s5, s6, a1, a2, a3, a4, a5, a6, potion1, potion2]
return itemsList
② 初始化勇士 Initadventurer()
带着名字就能创建一个勇士,勇士初始装备设置为新手套装,hp你看着改
def initAdventurer(name):
w1 = Equipments.Weapon('新手刀', 10, 1)
s1 = Equipments.Shield('新手盾', 2, 0.3, 1)
a1 = Equipments.Armour('新手甲', 3, 1)
potion1 = Potions("healthPotion")
potion2 = Potions("damagePotion")
myInventory = Inventory()
myInventory.getItem(potion1)
myInventory.getItem(potion2)
adventurer = Adventurer(name, w1, s1, a1, myInventory, 100)
return adventurer
(2) 战斗过程 battle
① 前三波初始化弱敌 initEnemy()
def initEnemy(enemyNum):
# 后续可以考虑随机怪兽的装备和初始血量
enemyName = f"怪物{
enemyNum}号"
w1 = Equipments.Weapon('新手刀', 10, 1)
s1 = Equipments.Shield('新手盾', 2, 0.3, 1)
a1 = Equipments.Armour('新手甲', 3, 1)
enemy = Adventurer(enemyName, w1, s1, a1, Inventory(), 50)
return enemy
② 随机加强敌人 RandomEnemy()
随机化的,取个随机数从物品库里面随机取就行,和后面奖励机制一样
def randomEnemy(enemyNum):
enemyName = f"怪物{
enemyNum}号"
wNum = random.randint(0, 5)
sNum = random.randint(6, 11)
aNum = random.randint(12, 17)
randomHP = random.randint(40+6*enemyNum, 80+6*enemyNum) # 这里用enemyNum系数加成
enemy = Adventurer(enemyName, itemsList[wNum], itemsList[sNum], itemsList[aNum], Inventory(), randomHP)
return enemy
③ 判断先手 回合战斗
if adv.getWeight() <= enemy.getWeight(): # 谁轻谁先手,一样你先手
while adv.isAlive() == "true" and enemy.isAlive() == "true": # 都活着就继续干
enemySub, advAdd, advSub, enemyAdd = 0, 0, 0, 0
enemySub, advAdd = actions.action(adv, enemy) # 冒险者打怪兽
if enemySub == -99 and advAdd == -99:
return
advSub, enemyAdd = actions.enemyAction(enemy, adv) # 怪兽打冒险者
settleRound(adv, enemy, enemySub, advAdd, advSub, enemyAdd) # 结算这一回合
else:
while adv.isAlive() == "true" and enemy.isAlive() == "true": # 都活着就继续干
enemySub, advAdd, advSub, enemyAdd = 0, 0, 0, 0
advSub, enemyAdd = actions.enemyAction(enemy, adv) # 怪兽打冒险者
enemySub, advAdd = actions.action(adv, enemy) # 冒险者打怪兽
if enemySub == -99 and advAdd == -99:
return
settleRound(adv, enemy, enemySub, advAdd, advSub, enemyAdd) # 结算这一回合
if enemy.alive == 'false':
print("===========================牛批,恭喜你战胜了怪兽===================================================")
win = 1
a) 勇士行为action
def action(adv, enemy):
eSub, aAdd = 0, 0
if adv.stunned == 'true':
print("你被对面用盾牌格挡眩晕了,将跳过这回合")
adv.stunned = 'false'
return eSub, aAdd
print("你有如下选择:")
print("1.攻击attack")
print("2.格挡block")
print("3.使用物品use an item")
print("4.逃跑run away")
while 1:
choice = input("请输入你的选择:")
# switch在python3.10的版本之前只能通过字典实现,此处不努力了。
if choice == "1":
eSub = attack(adv, enemy)
elif choice == "2":
aAdd = defend(adv, enemy)
elif choice == "3":
usePotion(adv, enemy)
elif choice == "4":
runAway()
return -99, -99
else:
print("输入指令无效,请重新输入:")
continue
break
return eSub, aAdd
攻击attack
def attack(attacker=initAdventurer("无衣"), victim=randomEnemy(0)):
attacker.blocking = 'false'
eSub = attacker.weapon.damage - victim.armour.armourRating
return eSub
格挡defend
def defend(attacker=initAdventurer("无衣"), victim=randomEnemy(0)):
attacker.blocking = "true" # 设置为格挡,直到冒险者选择攻击或者使用物品或者冒险者受到伤害。
aAdd = attacker.shield.armourRating
# 设置一个自由值(random number)在0到1之间,如果这个值等于盾牌眩晕对手的几率(percentage),设置对手的被控制状态(stunned)为true,对手必须跳过这一回合。
safePer = random.random() # 产生随机浮点数,小于等于概率就是晕了
if safePer <= attacker.shield.percentage:
print("============格挡成功并眩晕了=========")
victim.stunned = "true"
else:
print("==============格挡失败,不眩晕===========")
victim.stunned = "false"
return aAdd
用药usePotion
def usePotion(adv=initAdventurer("无衣"), enemy=randomEnemy(0)):
adv.blocking = 'false'
adv.showInventory()
while 1:
dex = input("请输入序号选择你要使用的物品(没有药的话用-1后退),战斗状态只能用药品,希望你不要不识好歹:")
if dex == "-1": # 没有药就让他重新选择干嘛,不然就死循环了
action(adv, enemy)
break
else:
dex = int(dex)
item = adv.inventory.items[dex - 1]
if "Potion" in item.name: # 如果是药品,就用
adv.inventory.items.pop(dex - 1) # 把物品从背包取出来
if item.name == "healthPotion":
adv.hp += 200
if adv.hp > adv.maxHP:
adv.hp = adv.maxHP
if item.name == "damagePotion":
enemy.hp -= 50
if enemy.hp <= 0:
enemy.alive = "false"
break
else:
print("重新选!只能使用药品")
逃跑runAway
def runAway():
print("不要气馁,再试试能不能抽到较弱的怪兽,实在不行就自杀吧。")
pass
b) 怪物行为enemyAction
怪物不能用药和逃跑,所以只有攻击和格挡
def enemyAction(enemy, adv):
aSub, eAdd = 0, 0
if enemy.stunned == 'true':
print("怪兽被你用盾牌格挡眩晕了,将跳过这回合")
enemy.stunned = 'false'
return aSub, eAdd
ran = random.randint(1, 2)
if ran == 1:
print("=======怪兽选择进攻======")
aSub = attack(enemy, adv)
if ran == 2:
print("=======怪兽选择格挡=====")
eAdd = defend(enemy, adv)
return aSub, eAdd
④ 结算回合
a) 结算勇士
# 结算勇士
if enemy.blocking == 'false': # 怪兽不格挡,才会攻击,才需要统计血量
if adv.blocking == 'true':
if advSub > advAdd: # 格挡且对面攻击护甲之后掉的血比盾牌挡的多
adv.hp = adv.hp - advSub + advAdd
elif advSub != 0:
adv.hp -= 1
else:
if advSub > 0:
adv.hp = adv.hp - advSub
elif advSub != 0: # 对面攻击没我护甲高,掉1 且怪兽不是格挡
adv.hp -= 1
else: # 怪兽格挡,不用管勇士血量了
pass
b) 结算怪物
# 结算怪物
if adv.blocking == 'false': # 勇士不格挡,才会攻击,才更新怪兽血量
if enemy.blocking == 'true':
if enemySub > enemyAdd: # 对面攻击护甲之后掉的血比盾牌挡的多
enemy.hp = enemy.hp - enemySub + enemyAdd
elif enemySub != 0: # 对面攻击护甲之后掉的血比盾牌挡的少
enemy.hp -= 1
else:
if enemySub > 0:
enemy.hp = enemy.hp - enemySub
elif enemySub != 0: # 我攻击没对面护甲高,掉1
enemy.hp -= 1
else: # 我格挡了,怪兽无所谓
pass
c) 展示状态并更新
# 刷新状态
adv.showStatus()
enemy.showStatus()
adv.blocking = 'false'
enemy.blocking = 'false'
if adv.hp <= 0:
adv.alive = 'false'
if enemy.hp <= 0:
enemy.alive = 'false'
(3) 清扫战场与装备管理 InventorManagement
def inventoryManagement(adv, itemsList):
global enemyNum
print("===========================即将迎接下一个怪兽,请做好准备===================================================")
empty0 = adv.inventory.empty
while 1:
print("你有如下选择:")
print("1.挑选胜利奖品pick up items")
print("2.丢弃物品栏物品drop held items")
print("3.装备物品栏物品equip held items")
print("4.继续战斗fight next enemy")
choice = input("请输入你的胜利选择(-1表示退出):")
# switch在python3.10的版本之前只能通过字典实现,此处不努力了。
if choice == "1":
if win == 1:
if empty0 - adv.inventory.empty < 3:
actions.pickUpItem(adv, itemsList)
else:
print("你已经挑选了三个奖品了,请不要利用漏洞多选哦~")
else:
print("\n----------------你击败了怪兽吗就想领奖励?逃跑可是没有战利品的------------------\n")
elif choice == "2":
actions.dropItem(adv)
elif choice == "3":
actions.equipItem(adv)
elif choice == "4":
enemyNum += 1
battle(adv)
elif choice == '-1':
break
else:
print("输入指令无效,请重新输入:")
continue
① 挑选战利品 pickUpItem()
def pickUpItem(adv, itemsList):
wNum = random.randint(0, 5)
sNum = random.randint(6, 11)
aNum = random.randint(12, 17)
potionNum = random.randint(18, 19)
winList = [itemsList[wNum], itemsList[sNum], itemsList[aNum], itemsList[potionNum]]
# print(winList)
for i in range(len(winList)):
item = winList[i]
print(f"item{
i + 1}:{
item.name}")
for i in range(1, 4):
dex = input("请输入序号选择你要的物品(-1返回上一级表示你不要物品,将直接开始战斗):")
# =======================有个bug,如果这里选1个2个出去了,其实就可以无限刷装备。=============
# 已修复:在pickUp的入口处加了个判断
if dex == "-1": # 上一级
break
elif len(re.findall("\d", dex)) == 0: # 如果不是数字
print("输入指令无效,请重新输入:")
continue
else:
ack = input("你确认要这件物品?(Y or N):")
if ack == "Y":
dex = int(dex)
item = winList[dex - 1]
adv.inventory.getItem(item) # 把物品从背包取出来
# adv.inventory.showBag()
else:
print("已取消捡起,请重新选择")
continue
② 丢弃物品 DropItem()
def dropItem(adv=initAdventurer(" ")):
adv.showInventory()
while 1:
dex = input("请输入序号选择你要丢弃的物品(-1返回上一级):")
if dex == "-1": # 上一级
break
else:
ack = input("你确认要丢这件物品?(Y or N)")
if ack == "Y":
dex = int(dex)
adv.inventory.items.pop(dex - 1) # 把物品从背包取出来
else:
print("已取消丢弃,请重新选择")
continue
③ 装备物品 equipItem()
def equipItem(adv):
adv.showInventory()
while 1:
dex = input("请输入序号选择你要装备的物品(没有装备的话用-1后退),不能用药品,希望你不要不识好歹:")
if dex == "-1": # 退出换装,不然就死循环了
print("退出换装")
break
else:
dex = int(dex)
item = adv.inventory.items[dex - 1]
if "Potion" in item.name: # 如果是药品
print("重新选!只能使用装备")
elif "sword" in item.name: # 如果是武器
# adv.inventory.items.pop(dex - 1) # 把物品从背包取出来
adv.inventory.dropItem(dex-1) # 把物品从背包取出来
adv.inventory.getItem(adv.weapon) # 把装备放进背包
adv.weapon = item # 把取出的装备换上
adv.showInventory()
continue
elif "shield" in item.name: # 如果是盾
# adv.inventory.items.pop(dex - 1) # 把物品从背包取出来
adv.inventory.dropItem(dex-1) # 把物品从背包取出来
adv.inventory.getItem(adv.weapon) # 把装备放进背包
adv.shield = item # 把取出的装备换上
adv.showInventory()
continue
elif "armour" in item.name: # 如果是护甲
# adv.inventory.items.pop(dex - 1) # 把物品从背包取出来
adv.inventory.dropItem(dex-1) # 把物品从背包取出来
adv.inventory.getItem(adv.weapon) # 把装备放进背包
adv.armour = item # 把取出的装备换上
adv.showInventory()
continue
else:
print("这是bug,可能是我给装备改名字了")
④ 继续战斗 battle()
elif choice == "4":
enemyNum += 1
battle(adv)
战斗结束条件
while adventurer.alive == "true":
3 主程序中main函数流程:
if __name__ == '__main__':
# test()
# 初始化一些参数
enemyNum = 0
itemsList = getItemsList()
# 创建勇士
name = input("欢迎来到冒险岛,请为勇士创建一个名字:")
adventurer = initAdventurer(name)
adventurer.showHero()
print()
print("欢迎来到新世界,努力打倒敌人变得更强吧,勇士,请踏上你的征程!")
print("*****************************************************************")
print()
while adventurer.alive == "true":
enemyNum += 1
battle(adventurer)
inventoryManagement(adventurer, itemsList)
附加文件
说明文档
装备属性
武器: 攻击力 重量
Redsword 15 1
Orangesword 20 1.5
Yellowsword 30 2
Greensword 40 2.5
Cyansword 50 3
Bluesword 60 3.5
Purplesword 70 4
Lightsword 80 4.5
Darksword 90 5
Excalibur 100 1
盾牌: 防御值 重量 眩晕值
Redshield 10 1.5 0.1
Orangeshield 15 2 0.15
Yellowshield 20 2.5 0.2
Greenshield 30 3 0.25
Cyanshield 40 3.5 0.3
Blueshield 50 4 0.35
Purpleshield 60 4.5 0.4
Lightshield 70 5 0.45
Darkshield 80 5.5 0.5
Mirrorshield 90 6 0.6
盔甲: 防御值 重量
Redarmour 10 2
Orangearmour 15 2.5
Yellowarmour 20 3
Greenarmour 30 3.5
Cyanarmour 40 4
Bluearmour 50 4.5
Purplearmour 60 5
Lightarmour 70 5.5
Darkarmour 80 6
Goldenarmour 100 8