|
|
|
|
import pygame
|
|
|
|
|
from settings import *
|
|
|
|
|
from support import import_folder
|
|
|
|
|
from entity import Entity
|
|
|
|
|
|
|
|
|
|
class Player(Entity):
|
|
|
|
|
def __init__(self,pos,groups,obstacle_sprites,create_attack,destroy_attack,create_magic):
|
|
|
|
|
# 调用父类的初始化方法
|
|
|
|
|
super().__init__(groups)
|
|
|
|
|
# 设置玩家图像和位置
|
|
|
|
|
self.image = pygame.image.load('../graphics/test/player.png').convert_alpha()
|
|
|
|
|
self.rect = self.image.get_rect(topleft = pos)
|
|
|
|
|
# 设置玩家的碰撞框
|
|
|
|
|
self.hitbox = self.rect.inflate(-6,HITBOX_OFFSET['player'])
|
|
|
|
|
|
|
|
|
|
# 图形设置
|
|
|
|
|
# 导入玩家的其他图形资源
|
|
|
|
|
self.import_player_assets()
|
|
|
|
|
# 状态设置
|
|
|
|
|
# 玩家初始状态朝向下
|
|
|
|
|
self.status = 'down'
|
|
|
|
|
|
|
|
|
|
# 移动和攻击相关设置
|
|
|
|
|
self.attacking = False
|
|
|
|
|
# 攻击冷却时间
|
|
|
|
|
self.attack_cooldown = 400
|
|
|
|
|
# 攻击时间
|
|
|
|
|
self.attack_time = None
|
|
|
|
|
# 障碍物精灵组
|
|
|
|
|
self.obstacle_sprites = obstacle_sprites
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 武器设置
|
|
|
|
|
self.create_attack = create_attack
|
|
|
|
|
self.destroy_attack = destroy_attack
|
|
|
|
|
self.weapon_index = 0
|
|
|
|
|
self.weapon = list(weapon_data.keys())[self.weapon_index]
|
|
|
|
|
# 初始武器
|
|
|
|
|
self.can_switch_weapon = True
|
|
|
|
|
self.weapon_switch_time = None
|
|
|
|
|
# 切换武器的冷却时间
|
|
|
|
|
self.switch_duration_cooldown = 200
|
|
|
|
|
|
|
|
|
|
# 魔法设置
|
|
|
|
|
self.create_magic = create_magic
|
|
|
|
|
self.magic_index = 0
|
|
|
|
|
self.magic = list(magic_data.keys())[self.magic_index]
|
|
|
|
|
# 初始魔法
|
|
|
|
|
self.can_switch_magic = True
|
|
|
|
|
self.magic_switch_time = None
|
|
|
|
|
# 玩家属性设置
|
|
|
|
|
# 初始属性
|
|
|
|
|
self.stats = {'health': 100,'energy':60,'attack': 10,'magic': 4,'speed': 5}
|
|
|
|
|
# 最大属性
|
|
|
|
|
self.max_stats = {'health': 300, 'energy': 140, 'attack': 20, 'magic' : 10, 'speed': 10}
|
|
|
|
|
# 升级消耗
|
|
|
|
|
self.upgrade_cost = {'health': 100, 'energy': 100, 'attack': 100, 'magic' : 100, 'speed': 100}
|
|
|
|
|
# 当前生命值
|
|
|
|
|
self.health = self.stats['health'] * 1.0
|
|
|
|
|
# 当前能量值
|
|
|
|
|
self.energy = self.stats['energy'] * 0.8
|
|
|
|
|
# 初始经验值
|
|
|
|
|
self.exp = 5000
|
|
|
|
|
# 移动速度
|
|
|
|
|
self.speed = self.stats['speed']
|
|
|
|
|
|
|
|
|
|
# 伤害计时器和无敌状态设置
|
|
|
|
|
# 是否可以受到伤害
|
|
|
|
|
self.vulnerable = True
|
|
|
|
|
# 受伤时间
|
|
|
|
|
self.hurt_time = None
|
|
|
|
|
# 无敌持续时间
|
|
|
|
|
self.invulnerability_duration = 500
|
|
|
|
|
|
|
|
|
|
# 导入武器攻击音效
|
|
|
|
|
self.weapon_attack_sound = pygame.mixer.Sound('../audio/sword.wav')
|
|
|
|
|
# 设置音量
|
|
|
|
|
self.weapon_attack_sound.set_volume(0.4)
|
|
|
|
|
|
|
|
|
|
def import_player_assets(self):
|
|
|
|
|
# 设置玩家动画资源的文件路径
|
|
|
|
|
character_path = '../graphics/player/'
|
|
|
|
|
# 初始化玩家动画字典,包含不同方向和状态的动画列表
|
|
|
|
|
self.animations = {'up': [],'down': [],'left': [],'right': [],
|
|
|
|
|
'right_idle':[],'left_idle':[],'up_idle':[],'down_idle':[],
|
|
|
|
|
'right_attack':[],'left_attack':[],'up_attack':[],'down_attack':[]}
|
|
|
|
|
# 遍历动画字典,加载对应路径下的动画帧
|
|
|
|
|
for animation in self.animations.keys():
|
|
|
|
|
full_path = character_path + animation
|
|
|
|
|
# 调用函数导入文件夹中的动画帧
|
|
|
|
|
self.animations[animation] = import_folder(full_path)
|
|
|
|
|
|
|
|
|
|
def input(self):
|
|
|
|
|
# 如果玩家当前未处于攻击状态
|
|
|
|
|
if not self.attacking:
|
|
|
|
|
# 获取当前按下的键
|
|
|
|
|
keys = pygame.key.get_pressed()
|
|
|
|
|
|
|
|
|
|
# 处理移动输入
|
|
|
|
|
# 按下向上键
|
|
|
|
|
if keys[pygame.K_UP]:
|
|
|
|
|
# 设置垂直方向向上移动
|
|
|
|
|
self.direction.y = -1
|
|
|
|
|
# 设置玩家状态为向上
|
|
|
|
|
self.status = 'up'
|
|
|
|
|
# 按下向下键
|
|
|
|
|
elif keys[pygame.K_DOWN]:
|
|
|
|
|
# 设置垂直方向向下移动
|
|
|
|
|
self.direction.y = 1
|
|
|
|
|
# 设置玩家状态为向下
|
|
|
|
|
self.status = 'down'
|
|
|
|
|
else:
|
|
|
|
|
# 停止垂直移动
|
|
|
|
|
self.direction.y = 0
|
|
|
|
|
# 按下向右键
|
|
|
|
|
if keys[pygame.K_RIGHT]:
|
|
|
|
|
# 设置水平方向向右移动
|
|
|
|
|
self.direction.x = 1
|
|
|
|
|
# 设置玩家状态为向右
|
|
|
|
|
self.status = 'right'
|
|
|
|
|
# 按下向左键
|
|
|
|
|
elif keys[pygame.K_LEFT]:
|
|
|
|
|
# 设置水平方向向左移动
|
|
|
|
|
self.direction.x = -1
|
|
|
|
|
# 设置玩家状态为向左
|
|
|
|
|
self.status = 'left'
|
|
|
|
|
else:
|
|
|
|
|
# 停止水平移动
|
|
|
|
|
self.direction.x = 0
|
|
|
|
|
|
|
|
|
|
# 处理攻击输入
|
|
|
|
|
# 按下空格键
|
|
|
|
|
if keys[pygame.K_SPACE]:
|
|
|
|
|
# 玩家开始攻击
|
|
|
|
|
self.attacking = True
|
|
|
|
|
# 记录攻击时间
|
|
|
|
|
self.attack_time = pygame.time.get_ticks()
|
|
|
|
|
# 创建攻击对象
|
|
|
|
|
self.create_attack()
|
|
|
|
|
# 播放武器攻击音效
|
|
|
|
|
self.weapon_attack_sound.play()
|
|
|
|
|
|
|
|
|
|
# 处理魔法输入
|
|
|
|
|
# 按下左Ctrl键
|
|
|
|
|
if keys[pygame.K_LCTRL]:
|
|
|
|
|
# 玩家开始攻击
|
|
|
|
|
self.attacking = True
|
|
|
|
|
# 记录攻击时间
|
|
|
|
|
self.attack_time = pygame.time.get_ticks()
|
|
|
|
|
# 获取当前魔法类型
|
|
|
|
|
style = list(magic_data.keys())[self.magic_index]
|
|
|
|
|
# 计算魔法强度
|
|
|
|
|
strength = list(magic_data.values())[self.magic_index]['strength'] + self.stats['magic']
|
|
|
|
|
# 获取魔法消耗
|
|
|
|
|
cost = list(magic_data.values())[self.magic_index]['cost']
|
|
|
|
|
# 创建魔法效果
|
|
|
|
|
self.create_magic(style,strength,cost)
|
|
|
|
|
# 处理切换武器输入
|
|
|
|
|
# 按下Q键且可以切换武器
|
|
|
|
|
if keys[pygame.K_q] and self.can_switch_weapon:
|
|
|
|
|
# 设置切换武器冷却状态为False
|
|
|
|
|
self.can_switch_weapon = False
|
|
|
|
|
# 记录切换武器时间
|
|
|
|
|
self.weapon_switch_time = pygame.time.get_ticks()
|
|
|
|
|
|
|
|
|
|
if self.weapon_index < len(list(weapon_data.keys())) - 1:
|
|
|
|
|
# 切换至下一个武器
|
|
|
|
|
self.weapon_index += 1
|
|
|
|
|
else:
|
|
|
|
|
# 回到第一个武器
|
|
|
|
|
self.weapon_index = 0
|
|
|
|
|
# 更新当前武器
|
|
|
|
|
self.weapon = list(weapon_data.keys())[self.weapon_index]
|
|
|
|
|
# 处理切换魔法输入
|
|
|
|
|
# 按下E键且可以切换魔法
|
|
|
|
|
if keys[pygame.K_e] and self.can_switch_magic:
|
|
|
|
|
# 设置切换魔法冷却状态为False
|
|
|
|
|
self.can_switch_magic = False
|
|
|
|
|
# 记录切换魔法时间
|
|
|
|
|
self.magic_switch_time = pygame.time.get_ticks()
|
|
|
|
|
|
|
|
|
|
if self.magic_index < len(list(magic_data.keys())) - 1:
|
|
|
|
|
# 切换至下一个魔法
|
|
|
|
|
self.magic_index += 1
|
|
|
|
|
else:
|
|
|
|
|
# 回到第一个魔法
|
|
|
|
|
self.magic_index = 0
|
|
|
|
|
# 更新当前魔法
|
|
|
|
|
self.magic = list(magic_data.keys())[self.magic_index]
|
|
|
|
|
|
|
|
|
|
def get_status(self):
|
|
|
|
|
|
|
|
|
|
# 空闲状态
|
|
|
|
|
# 如果角色没有移动且不在攻击状态下
|
|
|
|
|
if self.direction.x == 0 and self.direction.y == 0:
|
|
|
|
|
# 如果当前状态不包含 'idle' 和 'attack'
|
|
|
|
|
if not 'idle' in self.status and not 'attack' in self.status:
|
|
|
|
|
# 添加 '_idle' 到状态末尾,表示空闲状态
|
|
|
|
|
self.status = self.status + '_idle'
|
|
|
|
|
# 如果角色正在攻击
|
|
|
|
|
if self.attacking:
|
|
|
|
|
# 停止移动
|
|
|
|
|
self.direction.x = 0
|
|
|
|
|
self.direction.y = 0
|
|
|
|
|
# 如果当前状态不包含 'attack'
|
|
|
|
|
if not 'attack' in self.status:
|
|
|
|
|
# 如果当前状态包含 'idle'
|
|
|
|
|
if 'idle' in self.status:
|
|
|
|
|
# 将 'idle' 替换为 'attack'
|
|
|
|
|
self.status = self.status.replace('_idle','_attack')
|
|
|
|
|
else:
|
|
|
|
|
# 在当前状态末尾添加 '_attack'
|
|
|
|
|
self.status = self.status + '_attack'
|
|
|
|
|
# 如果角色不在攻击状态下
|
|
|
|
|
else:
|
|
|
|
|
# 如果当前状态包含 'attack'
|
|
|
|
|
if 'attack' in self.status:
|
|
|
|
|
# 移除 'attack',恢复到移动状态
|
|
|
|
|
self.status = self.status.replace('_attack','')
|
|
|
|
|
|
|
|
|
|
def cooldowns(self):
|
|
|
|
|
# 处理攻击冷却
|
|
|
|
|
current_time = pygame.time.get_ticks()
|
|
|
|
|
# 如果当前时间超过攻击冷却时间,则停止攻击
|
|
|
|
|
if self.attacking:
|
|
|
|
|
if current_time - self.attack_time >= self.attack_cooldown + weapon_data[self.weapon]['cooldown']:
|
|
|
|
|
self.attacking = False
|
|
|
|
|
self.destroy_attack()
|
|
|
|
|
# 处理武器切换冷却
|
|
|
|
|
if not self.can_switch_weapon:
|
|
|
|
|
# 如果当前时间超过武器切换冷却时间,则允许切换武器
|
|
|
|
|
if current_time - self.weapon_switch_time >= self.switch_duration_cooldown:
|
|
|
|
|
self.can_switch_weapon = True
|
|
|
|
|
# 处理魔法切换冷却
|
|
|
|
|
if not self.can_switch_magic:
|
|
|
|
|
# 如果当前时间超过魔法切换冷却时间,则允许切换魔法
|
|
|
|
|
if current_time - self.magic_switch_time >= self.switch_duration_cooldown:
|
|
|
|
|
self.can_switch_magic = True
|
|
|
|
|
# 处理受伤后的无敌时间
|
|
|
|
|
if not self.vulnerable:
|
|
|
|
|
# 如果当前时间超过无敌时间,则角色恢复可受伤状态
|
|
|
|
|
if current_time - self.hurt_time >= self.invulnerability_duration:
|
|
|
|
|
self.vulnerable = True
|
|
|
|
|
|
|
|
|
|
def animate(self):
|
|
|
|
|
# 获取当前状态对应的动画列表
|
|
|
|
|
animation = self.animations[self.status]
|
|
|
|
|
|
|
|
|
|
# 更新帧索引
|
|
|
|
|
self.frame_index += self.animation_speed
|
|
|
|
|
# 如果帧索引超过动画长度,则重置为0,实现循环播放
|
|
|
|
|
if self.frame_index >= len(animation):
|
|
|
|
|
self.frame_index = 0
|
|
|
|
|
|
|
|
|
|
# 根据帧索引设置当前图像
|
|
|
|
|
self.image = animation[int(self.frame_index)]
|
|
|
|
|
# 更新图像的位置
|
|
|
|
|
self.rect = self.image.get_rect(center = self.hitbox.center)
|
|
|
|
|
|
|
|
|
|
# 无敌状态下的闪烁效果
|
|
|
|
|
if not self.vulnerable:
|
|
|
|
|
# 使用波动值计算透明度
|
|
|
|
|
alpha = self.wave_value()
|
|
|
|
|
# 设置图像的透明度
|
|
|
|
|
self.image.set_alpha(alpha)
|
|
|
|
|
else:
|
|
|
|
|
# 恢复图像完全可见(不透明)
|
|
|
|
|
self.image.set_alpha(255)
|
|
|
|
|
|
|
|
|
|
def get_full_weapon_damage(self):
|
|
|
|
|
# 获取角色基础攻击力
|
|
|
|
|
base_damage = self.stats['attack']
|
|
|
|
|
# 获取当前武器的伤害值
|
|
|
|
|
weapon_damage = weapon_data[self.weapon]['damage']
|
|
|
|
|
# 返回角色使用当前武器的总伤害值
|
|
|
|
|
return base_damage + weapon_damage
|
|
|
|
|
|
|
|
|
|
def get_full_magic_damage(self):
|
|
|
|
|
# 获取角色基础魔法伤害
|
|
|
|
|
base_damage = self.stats['magic']
|
|
|
|
|
# 获取当前魔法的伤害值
|
|
|
|
|
spell_damage = magic_data[self.magic]['strength']
|
|
|
|
|
# 返回角色使用当前魔法的总伤害值
|
|
|
|
|
return base_damage + spell_damage
|
|
|
|
|
|
|
|
|
|
def get_value_by_index(self,index):
|
|
|
|
|
return list(self.stats.values())[index]
|
|
|
|
|
|
|
|
|
|
def get_cost_by_index(self,index):
|
|
|
|
|
return list(self.upgrade_cost.values())[index]
|
|
|
|
|
|
|
|
|
|
def energy_recovery(self):
|
|
|
|
|
if self.energy < self.stats['energy']:
|
|
|
|
|
# 根据魔法属性恢复能量
|
|
|
|
|
self.energy += 0.01 * self.stats['magic']
|
|
|
|
|
else:
|
|
|
|
|
# 能量恢复到最大值
|
|
|
|
|
self.energy = self.stats['energy']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_death(self):
|
|
|
|
|
# 如果敌人角色的生命值小于等于 0,表示角色已经死亡
|
|
|
|
|
if self.health <= 0:
|
|
|
|
|
# 从精灵组中移除角色,通常用于删除角色或停止其更新和渲染
|
|
|
|
|
self.kill()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def update(self):
|
|
|
|
|
# 处理用户输入
|
|
|
|
|
self.input()
|
|
|
|
|
# 处理冷却时间
|
|
|
|
|
self.cooldowns()
|
|
|
|
|
# 更新角色状态
|
|
|
|
|
self.get_status()
|
|
|
|
|
# 更新角色动画
|
|
|
|
|
self.animate()
|
|
|
|
|
# 移动角色
|
|
|
|
|
self.move(self.stats['speed'])
|
|
|
|
|
# 恢复能量
|
|
|
|
|
self.energy_recovery()
|
|
|
|
|
#死亡判断
|
|
|
|
|
self.check_death()
|