|
|
@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
import pygame
|
|
|
|
|
|
|
|
from settings import *
|
|
|
|
|
|
|
|
from tile import Tile
|
|
|
|
|
|
|
|
from player import Player
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from support import *
|
|
|
|
|
|
|
|
from random import choice, randint
|
|
|
|
|
|
|
|
from weapon import Weapon
|
|
|
|
|
|
|
|
from ui import UI
|
|
|
|
|
|
|
|
from enemy import Enemy
|
|
|
|
|
|
|
|
from particles import AnimationPlayer
|
|
|
|
|
|
|
|
from magic import MagicPlayer
|
|
|
|
|
|
|
|
from upgrade import Upgrade
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Level:
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 获取游戏窗口的显示表面(surface)
|
|
|
|
|
|
|
|
self.display_surface = pygame.display.get_surface()
|
|
|
|
|
|
|
|
# 标记游戏是否处于暂停状态
|
|
|
|
|
|
|
|
self.game_paused = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 可见精灵组,用于管理在摄像机视野内可见的精灵
|
|
|
|
|
|
|
|
self.visible_sprites = YSortCameraGroup()
|
|
|
|
|
|
|
|
# 障碍物精灵组,用于管理所有的障碍物精灵
|
|
|
|
|
|
|
|
self.obstacle_sprites = pygame.sprite.Group()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 当前攻击对象
|
|
|
|
|
|
|
|
self.current_attack = None
|
|
|
|
|
|
|
|
# 攻击精灵组,用于管理所有的攻击精灵
|
|
|
|
|
|
|
|
self.attack_sprites = pygame.sprite.Group()
|
|
|
|
|
|
|
|
# 可被攻击的精灵组,用于管理所有可以被攻击的精灵
|
|
|
|
|
|
|
|
self.attackable_sprites = pygame.sprite.Group()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 创建地图
|
|
|
|
|
|
|
|
self.create_map()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# UI 对象,用于管理游戏界面的显示和交互
|
|
|
|
|
|
|
|
self.ui = UI()
|
|
|
|
|
|
|
|
# 升级对象,与玩家角色相关联,用于管理升级和技能
|
|
|
|
|
|
|
|
self.upgrade = Upgrade(self.player)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 动画播放器对象,用于管理游戏中的动画效果
|
|
|
|
|
|
|
|
self.animation_player = AnimationPlayer()
|
|
|
|
|
|
|
|
# 魔法播放器对象,与动画播放器相关联,用于管理游戏中的魔法效果
|
|
|
|
|
|
|
|
self.magic_player = MagicPlayer(self.animation_player)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_map(self):
|
|
|
|
|
|
|
|
# 定义不同类型的地图布局和图形资源路径
|
|
|
|
|
|
|
|
layouts = {
|
|
|
|
|
|
|
|
'boundary': import_csv_layout('../map/map_FloorBlocks.csv'),
|
|
|
|
|
|
|
|
'grass': import_csv_layout('../map/map_Grass.csv'),
|
|
|
|
|
|
|
|
'object': import_csv_layout('../map/map_Objects.csv'),
|
|
|
|
|
|
|
|
'entities': import_csv_layout('../map/map_Entities.csv')
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
graphics = {
|
|
|
|
|
|
|
|
'grass': import_folder('../graphics/Grass'),
|
|
|
|
|
|
|
|
'objects': import_folder('../graphics/objects')
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
# 遍历不同的地图布局和样式
|
|
|
|
|
|
|
|
for style,layout in layouts.items():
|
|
|
|
|
|
|
|
# 遍历地图布局的每一行
|
|
|
|
|
|
|
|
for row_index,row in enumerate(layout):
|
|
|
|
|
|
|
|
# 遍历每行中的每个元素(列
|
|
|
|
|
|
|
|
for col_index, col in enumerate(row):
|
|
|
|
|
|
|
|
# 如果元素不为'-1',表示有地图元素需要创建
|
|
|
|
|
|
|
|
if col != '-1':
|
|
|
|
|
|
|
|
# 计算元素在屏幕上的位置(像素坐标)
|
|
|
|
|
|
|
|
x = col_index * TILESIZE
|
|
|
|
|
|
|
|
y = row_index * TILESIZE
|
|
|
|
|
|
|
|
# 边界类型的地图元素
|
|
|
|
|
|
|
|
if style == 'boundary':
|
|
|
|
|
|
|
|
# 创建障碍物精灵对象,设置为'invisible'类型(不可见)
|
|
|
|
|
|
|
|
Tile((x,y),[self.obstacle_sprites],'invisible')
|
|
|
|
|
|
|
|
# 草地类型的地图元素
|
|
|
|
|
|
|
|
if style == 'grass':
|
|
|
|
|
|
|
|
# 从草地图形资源中随机选择一个图像
|
|
|
|
|
|
|
|
random_grass_image = choice(graphics['grass'])
|
|
|
|
|
|
|
|
# 创建草地精灵对象,加入可见精灵组、障碍物精灵组和可攻击精灵组
|
|
|
|
|
|
|
|
Tile(
|
|
|
|
|
|
|
|
(x,y),
|
|
|
|
|
|
|
|
[self.visible_sprites,self.obstacle_sprites,self.attackable_sprites],
|
|
|
|
|
|
|
|
'grass',
|
|
|
|
|
|
|
|
random_grass_image)
|
|
|
|
|
|
|
|
# 物体类型的地图元素
|
|
|
|
|
|
|
|
if style == 'object':
|
|
|
|
|
|
|
|
# 根据列数获取物体图形资源,创建物体精灵对象
|
|
|
|
|
|
|
|
surf = graphics['objects'][int(col)]
|
|
|
|
|
|
|
|
Tile((x,y),[self.visible_sprites,self.obstacle_sprites],'object',surf)
|
|
|
|
|
|
|
|
# 实体类型的地图元素(角色和敌人)
|
|
|
|
|
|
|
|
if style == 'entities':
|
|
|
|
|
|
|
|
# 玩家角色
|
|
|
|
|
|
|
|
if col == '394':
|
|
|
|
|
|
|
|
# 创建玩家对象,并加入可见精灵组
|
|
|
|
|
|
|
|
self.player = Player(
|
|
|
|
|
|
|
|
(x,y),
|
|
|
|
|
|
|
|
[self.visible_sprites],
|
|
|
|
|
|
|
|
self.obstacle_sprites,
|
|
|
|
|
|
|
|
self.create_attack,
|
|
|
|
|
|
|
|
self.destroy_attack,
|
|
|
|
|
|
|
|
self.create_magic)
|
|
|
|
|
|
|
|
# 敌人角色
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
# 根据不同的角色编号选择对应的怪物名称
|
|
|
|
|
|
|
|
if col == '390': monster_name = 'bamboo'
|
|
|
|
|
|
|
|
elif col == '391': monster_name = 'spirit'
|
|
|
|
|
|
|
|
elif col == '392': monster_name ='raccoon'
|
|
|
|
|
|
|
|
else: monster_name = 'squid'
|
|
|
|
|
|
|
|
# 创建敌人对象,并加入可见精灵组和可攻击精灵组
|
|
|
|
|
|
|
|
Enemy(
|
|
|
|
|
|
|
|
monster_name,
|
|
|
|
|
|
|
|
(x,y),
|
|
|
|
|
|
|
|
[self.visible_sprites,self.attackable_sprites],
|
|
|
|
|
|
|
|
self.obstacle_sprites,
|
|
|
|
|
|
|
|
self.damage_player,
|
|
|
|
|
|
|
|
self.trigger_death_particles,
|
|
|
|
|
|
|
|
self.add_exp)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 创建武器对象,将其设为当前攻击对象
|
|
|
|
|
|
|
|
def create_attack(self):
|
|
|
|
|
|
|
|
# 该方法用于创建武器对象,并将其设为当前攻击对象。
|
|
|
|
|
|
|
|
self.current_attack = Weapon(self.player,[self.visible_sprites,self.attack_sprites])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_magic(self,style,strength,cost):
|
|
|
|
|
|
|
|
if style == 'heal':
|
|
|
|
|
|
|
|
# 创建治疗魔法效果
|
|
|
|
|
|
|
|
self.magic_player.heal(self.player,strength,cost,[self.visible_sprites])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if style == 'flame':
|
|
|
|
|
|
|
|
# 创建火焰魔法效果
|
|
|
|
|
|
|
|
self.magic_player.flame(self.player,cost,[self.visible_sprites,self.attack_sprites])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def destroy_attack(self):
|
|
|
|
|
|
|
|
# 检查当前是否存在攻击对象
|
|
|
|
|
|
|
|
if self.current_attack:
|
|
|
|
|
|
|
|
# 销毁当前攻击对象
|
|
|
|
|
|
|
|
self.current_attack.kill()
|
|
|
|
|
|
|
|
# 将当前攻击对象设置为 None
|
|
|
|
|
|
|
|
self.current_attack = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def player_attack_logic(self):
|
|
|
|
|
|
|
|
# 检查是否存在攻击精灵
|
|
|
|
|
|
|
|
if self.attack_sprites:
|
|
|
|
|
|
|
|
# 遍历每个攻击精灵
|
|
|
|
|
|
|
|
for attack_sprite in self.attack_sprites:
|
|
|
|
|
|
|
|
# 检测当前攻击精灵与可攻击精灵组中的精灵是否发生碰撞
|
|
|
|
|
|
|
|
collision_sprites = pygame.sprite.spritecollide(attack_sprite,self.attackable_sprites,False)
|
|
|
|
|
|
|
|
# 如果发生碰撞,遍历碰撞到的每个精灵
|
|
|
|
|
|
|
|
if collision_sprites:
|
|
|
|
|
|
|
|
for target_sprite in collision_sprites:
|
|
|
|
|
|
|
|
# 如果碰撞到的是草地精灵
|
|
|
|
|
|
|
|
if target_sprite.sprite_type == 'grass':
|
|
|
|
|
|
|
|
# 获取草地精灵的中心位置和偏移量
|
|
|
|
|
|
|
|
pos = target_sprite.rect.center
|
|
|
|
|
|
|
|
offset = pygame.math.Vector2(0,75)
|
|
|
|
|
|
|
|
# 随机生成一定数量的草叶粒子效果
|
|
|
|
|
|
|
|
for leaf in range(randint(3,6)):
|
|
|
|
|
|
|
|
self.animation_player.create_grass_particles(pos - offset,[self.visible_sprites])
|
|
|
|
|
|
|
|
# 销毁草地精灵
|
|
|
|
|
|
|
|
target_sprite.kill()
|
|
|
|
|
|
|
|
# 如果碰撞到的是其他类型的精灵,执行造成伤害的操作
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
target_sprite.get_damage(self.player,attack_sprite.sprite_type)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def damage_player(self,amount,attack_type):
|
|
|
|
|
|
|
|
# 如果玩家处于可受伤状态
|
|
|
|
|
|
|
|
if self.player.vulnerable:
|
|
|
|
|
|
|
|
# 减少玩家的健康值
|
|
|
|
|
|
|
|
self.player.health -= amount
|
|
|
|
|
|
|
|
# 设置玩家为无敌状态
|
|
|
|
|
|
|
|
self.player.vulnerable = False
|
|
|
|
|
|
|
|
# 记录玩家受伤的时间
|
|
|
|
|
|
|
|
self.player.hurt_time = pygame.time.get_ticks()
|
|
|
|
|
|
|
|
# 创建攻击类型的粒子效果
|
|
|
|
|
|
|
|
self.animation_player.create_particles(attack_type,self.player.rect.center,[self.visible_sprites])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def trigger_death_particles(self,pos,particle_type):
|
|
|
|
|
|
|
|
# 创建指定类型的死亡粒子效果
|
|
|
|
|
|
|
|
self.animation_player.create_particles(particle_type,pos,self.visible_sprites)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_exp(self,amount):
|
|
|
|
|
|
|
|
# 增加玩家的经验值
|
|
|
|
|
|
|
|
self.player.exp += amount
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def toggle_menu(self):
|
|
|
|
|
|
|
|
# 切换游戏菜单状态(暂停或继续)
|
|
|
|
|
|
|
|
self.game_paused = not self.game_paused
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
|
|
|
# 绘制可见精灵和玩家
|
|
|
|
|
|
|
|
self.visible_sprites.custom_draw(self.player)
|
|
|
|
|
|
|
|
# 显示用户界面(UI)
|
|
|
|
|
|
|
|
self.ui.display(self.player)
|
|
|
|
|
|
|
|
# 如果游戏暂停,显示升级界面
|
|
|
|
|
|
|
|
if self.game_paused:
|
|
|
|
|
|
|
|
self.upgrade.display()
|
|
|
|
|
|
|
|
# 如果游戏未暂停,更新可见精灵组
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
self.visible_sprites.update()
|
|
|
|
|
|
|
|
# 更新敌人精灵状态
|
|
|
|
|
|
|
|
self.visible_sprites.enemy_update(self.player)
|
|
|
|
|
|
|
|
# 处理玩家攻击逻辑
|
|
|
|
|
|
|
|
self.player_attack_logic()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class YSortCameraGroup(pygame.sprite.Group):
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
|
|
# 调用父类的初始化方法,初始化精灵组
|
|
|
|
|
|
|
|
super().__init__()
|
|
|
|
|
|
|
|
# 获取当前 Pygame 显示的表面(屏幕)
|
|
|
|
|
|
|
|
self.display_surface = pygame.display.get_surface()
|
|
|
|
|
|
|
|
# 计算屏幕宽度的一半
|
|
|
|
|
|
|
|
self.half_width = self.display_surface.get_size()[0] // 2
|
|
|
|
|
|
|
|
# 计算屏幕高度的一半
|
|
|
|
|
|
|
|
self.half_height = self.display_surface.get_size()[1] // 2
|
|
|
|
|
|
|
|
# 创建一个空的 Vector2 对象,用于表示摄像机的偏移量
|
|
|
|
|
|
|
|
self.offset = pygame.math.Vector2()
|
|
|
|
|
|
|
|
# 加载地板图像并转换为适合显示的格式
|
|
|
|
|
|
|
|
self.floor_surf = pygame.image.load('../graphics/tilemap/ground.png').convert()
|
|
|
|
|
|
|
|
# 获取地板图像的矩形区域(左上角位置为 (0, 0))
|
|
|
|
|
|
|
|
self.floor_rect = self.floor_surf.get_rect(topleft = (0,0))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def custom_draw(self,player):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 获取摄像机偏移量
|
|
|
|
|
|
|
|
self.offset.x = player.rect.centerx - self.half_width
|
|
|
|
|
|
|
|
self.offset.y = player.rect.centery - self.half_height
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 绘制地板
|
|
|
|
|
|
|
|
floor_offset_pos = self.floor_rect.topleft - self.offset
|
|
|
|
|
|
|
|
self.display_surface.blit(self.floor_surf,floor_offset_pos)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 按精灵的中心 Y 坐标排序并绘制精灵
|
|
|
|
|
|
|
|
for sprite in sorted(self.sprites(),key = lambda sprite: sprite.rect.centery):
|
|
|
|
|
|
|
|
# 计算精灵在屏幕上的绘制位置
|
|
|
|
|
|
|
|
offset_pos = sprite.rect.topleft - self.offset
|
|
|
|
|
|
|
|
# 在屏幕上绘制精灵图像
|
|
|
|
|
|
|
|
self.display_surface.blit(sprite.image,offset_pos)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def enemy_update(self,player):
|
|
|
|
|
|
|
|
# 从精灵组中筛选出所有的敌人精灵
|
|
|
|
|
|
|
|
enemy_sprites = [sprite for sprite in self.sprites() if hasattr(sprite,'sprite_type') and sprite.sprite_type == 'enemy']
|
|
|
|
|
|
|
|
# 遍历所有的敌人精灵
|
|
|
|
|
|
|
|
for enemy in enemy_sprites:
|
|
|
|
|
|
|
|
# 调用每个敌人精灵的特定更新方法,传递玩家角色作为参数
|
|
|
|
|
|
|
|
enemy.enemy_update(player)
|