You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
9.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import pygame
from settings import *
from entity import Entity
from support import *
class Enemy(Entity):
def __init__(self,monster_name,pos,groups,obstacle_sprites,damage_player,trigger_death_particles,add_exp):
# general setup
# 调用父类的初始化方法,将实例添加到指定的分组中
super().__init__(groups)
# 设置角色的精灵类型为 'enemy'
self.sprite_type = 'enemy'
# graphics setup
# 导入指定怪物名称的图像资源
self.import_graphics(monster_name)
# 设置角色的状态为 'idle'(空闲状态)
self.status = 'idle'
# 获取角色当前状态的图像
self.image = self.animations[self.status][self.frame_index]
# movement
# 设置角色的位置
self.rect = self.image.get_rect(topleft = pos)
# 设置角色的碰撞框,稍微向上收缩一些
self.hitbox = self.rect.inflate(0,-10)
# 设置角色所在的障碍物精灵组
self.obstacle_sprites = obstacle_sprites
# stats 将怪物的各种值赋予
self.monster_name = monster_name
# 从怪物数据字典中获取指定怪物的信息
monster_info = monster_data[self.monster_name]
# 怪物的生命值
self.health = monster_info['health']
# 击败怪物后获得的经验值
self.exp = monster_info['exp']
# 怪物的移动速度
self.speed = monster_info['speed']
# 怪物的攻击伤害
self.attack_damage = monster_info['damage']
# 怪物的抵抗力
self.resistance = monster_info['resistance']
# 怪物的攻击范围
self.attack_radius = monster_info['attack_radius']
# 怪物的发现玩家范围
self.notice_radius = monster_info['notice_radius']
# 怪物的攻击类型
self.attack_type = monster_info['attack_type']
# player interaction 设置角色与玩家交互相关的属性和方法
# 角色是否能够进行攻击
self.can_attack = True
# 记录上次攻击的时间
self.attack_time = None
# 攻击的冷却时间(毫秒)
self.attack_cooldown = 400
# 对玩家造成伤害的方法
self.damage_player = damage_player
# 触发角色死亡特效的方法
self.trigger_death_particles = trigger_death_particles
# 增加玩家经验值的方法
self.add_exp = add_exp
# invincibility timer 设置角色的无敌时间相关属性
# 角色是否处于易受伤状态
self.vulnerable = True
# 记录角色受到攻击的时间点
self.hit_time = None
# 角色的无敌持续时间(毫秒)
self.invincibility_duration = 300
# sounds 设置角色相关的音效
# 死亡音效
self.death_sound = pygame.mixer.Sound('../audio/death.wav')
# 受伤音效
self.hit_sound = pygame.mixer.Sound('../audio/hit.wav')
# 攻击音效
self.attack_sound = pygame.mixer.Sound(monster_info['attack_sound'])
# 设置死亡音效的音量
self.death_sound.set_volume(0.6)
# 设置受伤音效的音量
self.hit_sound.set_volume(0.6)
# 设置攻击音效的音量
self.attack_sound.set_volume(0.6)
def import_graphics(self,name):
# 初始化动画字典包含三种动画状态idle空闲、move移动、attack攻击
self.animations = {'idle':[],'move':[],'attack':[]}
# 指定敌人角色图形资源的主路径,根据角色名称构建路径
main_path = f'../graphics/monsters/{name}/'
# 调用 import_folder 函数导入指定路径下的图像资源,并将结果存储到动画字典中对应状态的键中
for animation in self.animations.keys():
self.animations[animation] = import_folder(main_path + animation)
def get_player_distance_direction(self,player):
# 获取敌人角色和玩家角色的中心点坐标作为向量
enemy_vec = pygame.math.Vector2(self.rect.center)
player_vec = pygame.math.Vector2(player.rect.center)
# 计算敌人角色与玩家角色之间的距离
distance = (player_vec - enemy_vec).magnitude()
# 如果距离大于 0则计算方向向量
if distance > 0:
# 计算方向向量并归一化
direction = (player_vec - enemy_vec).normalize()
# 距离为 0 或负数时,方向向量为零向量
else:
direction = pygame.math.Vector2()
# 返回距离和方向向量的元组
return (distance,direction)
def get_status(self, player):
# 获取敌人角色与玩家角色的距离
distance = self.get_player_distance_direction(player)[0]
# 根据距离和设定的攻击范围、发现范围确定敌人角色的状态
if distance <= self.attack_radius and self.can_attack:
# 如果距离小于等于攻击范围并且可以进行攻击
if self.status != 'attack':
# 重置动画帧索引
self.frame_index = 0
# 设置状态为攻击
self.status = 'attack'
# 如果距离小于等于发现范围
elif distance <= self.notice_radius:
# 设置状态为移动
self.status = 'move'
# 距离大于发现范围
else:
# 设置状态为空闲(静止)
self.status = 'idle'
def actions(self,player):
# 如果状态为攻击
if self.status == 'attack':
# 记录攻击时间
self.attack_time = pygame.time.get_ticks()
# 对玩家造成伤害
self.damage_player(self.attack_damage,self.attack_type)
# 播放攻击音效
self.attack_sound.play()
# 如果状态为移动
elif self.status == 'move':
# 获取朝向玩家的方向向量
self.direction = self.get_player_distance_direction(player)[1]
# 其他情况(状态为空闲或其他)
else:
# 将方向向量设为零向量,即停止移动
self.direction = pygame.math.Vector2()
def animate(self):
# 获取当前状态对应的动画帧列表
animation = self.animations[self.status]
# 更新帧索引,控制动画播放速度
self.frame_index += self.animation_speed
# 如果帧索引超过动画帧列表的长度
if self.frame_index >= len(animation):
# 如果是攻击状态,设置为不可攻击状态
if self.status == 'attack':
self.can_attack = False
# 重置帧索引,从头开始播放动画
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)
# 如果角色不是易受伤状态,则设置完全不透明(透明度为 255
else:
self.image.set_alpha(255)
# 处理敌人角色的攻击冷却和无敌状态的持续时间。
def cooldowns(self):
# 获取当前时间(以毫秒为单位)
current_time = pygame.time.get_ticks()
# 处理攻击冷却时间
# 如果当前不能攻击(即处于攻击冷却中)
if not self.can_attack:
# 如果距离上次攻击时间已经超过攻击冷却时间
if current_time - self.attack_time >= self.attack_cooldown:
# 设置为可以再次攻击
self.can_attack = True
# 处理无敌状态持续时间
# 如果当前不可受伤(即处于无敌状态)
if not self.vulnerable:
# 如果距离上次受伤时间已经超过无敌状态持续时间
if current_time - self.hit_time >= self.invincibility_duration:
# 设置为可受伤状态(结束无敌状态)
self.vulnerable = True
# 处理敌人角色受到玩家攻击造成的伤害。
def get_damage(self,player,attack_type):
if self.vulnerable:
# 播放受击音效
self.hit_sound.play()
# 设置受击后的朝向玩家的方向向量
self.direction = self.get_player_distance_direction(player)[1]
# 根据攻击类型应用不同的伤害值
if attack_type == 'weapon':
# 玩家使用武器攻击造成伤害
self.health -= player.get_full_weapon_damage()
# 玩家使用魔法攻击造成伤害
else:
self.health -= player.get_full_magic_damage()
# 记录受击时间
self.hit_time = pygame.time.get_ticks()
# 设置为不可受伤状态,进入无敌状态
self.vulnerable = False
#检查角色是否死亡
def check_death(self):
# 如果敌人角色的生命值小于等于 0表示角色已经死亡
if self.health <= 0:
# 从精灵组中移除角色,通常用于删除角色或停止其更新和渲染
self.kill()
# 触发死亡粒子效果,通常用于播放死亡动画或粒子效果
self.trigger_death_particles(self.rect.center,self.monster_name)
# 增加经验值
self.add_exp(self.exp)
# 播放死亡音效,提供游戏中的音效反馈
self.death_sound.play()
#被攻击后的反应
def hit_reaction(self):
# 如果敌人角色不可受伤
if not self.vulnerable:
# 则将角色的移动方向向量乘以负阻力,模拟受击后的击退效果。
self.direction *= -self.resistance
#更新状态
def update(self):
# 处理受击反应
self.hit_reaction()
# 移动角色,传入速度参数
self.move(self.speed)
# 更新角色动画
self.animate()
# 处理攻击冷却和无敌状态持续时间
self.cooldowns()
# 检查角色是否死亡并执行死亡逻辑
self.check_death()
def enemy_update(self,player):
# 获取敌人角色的状态
self.get_status(player)
# 执行敌人角色的行为
self.actions(player)