import pygame
import sys
import os
import random


def startInterface(screen, begin_images):
    begin_image = begin_images[0]
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                QuitGame()
            elif event.type == pygame.MOUSEMOTION:
                mouse_pos = pygame.mouse.get_pos()
                if mouse_pos[0] in list(range(419, 574)) and mouse_pos[1] in list(range(374, 416)):
                    begin_image = begin_images[1]
                else:
                    begin_image = begin_images[0]
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                if event.button == 1 and mouse_pos[0] in list(range(419, 574)) and mouse_pos[1] in list(
                        range(374, 416)):
                    return True
        screen.blit(begin_image, (0, 0))
        pygame.display.update()


def endInterface(screen, end_image, again_images, score_info, font_path, font_colors, screensize):
    again_image = again_images[0]
    font = pygame.font.Font(font_path, 50)
    your_score_text = font.render('Your Score: %s' % score_info['your_score'], True, font_colors[0])
    your_score_rect = your_score_text.get_rect()
    your_score_rect.left, your_score_rect.top = (screensize[0] - your_score_rect.width) / 2, 215
    best_score_text = font.render('Best Score: %s' % score_info['best_score'], True, font_colors[1])
    best_score_rect = best_score_text.get_rect()
    best_score_rect.left, best_score_rect.top = (screensize[0] - best_score_rect.width) / 2, 275
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                QuitGame()
            elif event.type == pygame.MOUSEMOTION:
                mouse_pos = pygame.mouse.get_pos()
                if mouse_pos[0] in list(range(419, 574)) and mouse_pos[1] in list(range(374, 416)):
                    again_image = again_images[1]
                else:
                    again_image = again_images[0]
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                if event.button == 1 and mouse_pos[0] in list(range(419, 574)) and mouse_pos[1] in list(
                        range(374, 416)):
                    return True
        screen.blit(end_image, (0, 0))
        screen.blit(again_image, (416, 370))
        screen.blit(your_score_text, your_score_rect)
        screen.blit(best_score_text, best_score_rect)
        pygame.display.update()

class PygameBaseGame():
    def __init__(self, config, **kwargs):
        # 设置属性
        self.config = config
        # 初始化
        self.initialize()
        # 用户可以覆盖默认参数
        for key, value in kwargs.items():
            if hasattr(self, key): setattr(self, key, value)

    ##############################在PygameBaseGame类下，__init__()函数后面，添加下列新的功能函数###################
    '''运行游戏'''

    def run(self):
        raise NotImplementedError('not to be implemented...')

    '''初始化'''

    def initialize(self):
        self.screen = InitPygame(screensize=self.config.SCREENSIZE, title=self.config.TITLE)
        bgm_path = self.config.BGM_PATH if hasattr(self.config, 'BGM_PATH') else None
        font_paths_dict = self.config.FONT_PATHS_DICT if hasattr(self.config, 'FONT_PATHS_DICT') else None
        image_paths_dict = self.config.IMAGE_PATHS_DICT if hasattr(self.config, 'IMAGE_PATHS_DICT') else None
        sound_paths_dict = self.config.SOUND_PATHS_DICT if hasattr(self.config, 'SOUND_PATHS_DICT') else None
        self.resource_loader = PygameResourceLoader(
            bgm_path=bgm_path,
            font_paths_dict=font_paths_dict,
            image_paths_dict=image_paths_dict,
            sound_paths_dict=sound_paths_dict,
        )

class PygameResourceLoader():
    def __init__(self, image_paths_dict=None, sound_paths_dict=None, font_paths_dict=None, bgm_path=None, **kwargs):
        # 设置属性
        self.bgm_path = bgm_path
        self.font_paths_dict = font_paths_dict
        self.image_paths_dict = image_paths_dict
        self.sound_paths_dict = sound_paths_dict
        # 导入字体
        self.fonts = self.fontload(font_paths_dict)
        # 导入图像
        self.images = self.defaultload(image_paths_dict, pygame.image.load)
        # 导入声音
        self.sounds = self.defaultload(sound_paths_dict, pygame.mixer.Sound)

    ##############################在PygameResourceLoader类下，__init__()函数后面，添加下列新的功能函数###################
    '''默认的素材导入函数'''

    def defaultload(self, resources_dict, load_func):
        if resources_dict is None: return dict()
        assert isinstance(resources_dict, dict)
        resources = dict()
        for key, value in resources_dict.items():
            if isinstance(value, dict):
                resources[key] = self.defaultload(value, load_func)
            elif isinstance(value, list):
                resources[key] = list()
                for path in value: resources[key].append(load_func(path))
            else:
                resources[key] = load_func(value)
        return resources

    '''导入字体'''

    def fontload(self, font_paths_dict):
        if font_paths_dict is None: return dict()
        assert isinstance(font_paths_dict, dict)
        fonts = dict()
        for key, value in font_paths_dict.items():
            if not value.get('system_font', False):
                fonts[key] = pygame.font.Font(value['name'], value['size'])
            else:
                fonts[key] = pygame.font.SysFont(value['name'], value['size'])
        return fonts

    '''播放背景音乐'''

    def playbgm(self):
        pygame.mixer.music.load(self.bgm_path)
        pygame.mixer.music.play(-1, 0.0)

class Mole(pygame.sprite.Sprite):
    def __init__(self, images, position, **kwargs):
        pygame.sprite.Sprite.__init__(self)
        self.images = [
            pygame.transform.scale(images[0], (101, 103)),
            pygame.transform.scale(images[-1], (101, 103))
        ]
        self.image = self.images[0]
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)
        self.setPosition(position)
        self.is_hammer = False

    ##############################在Mole类下，__init__()函数后面，添加下列新的功能函数###################
    '''设置位置'''

    def setPosition(self, pos):
        self.rect.left, self.rect.top = pos

    '''设置被击中'''

    def setBeHammered(self):
        self.is_hammer = True

    '''显示在屏幕上'''

    def draw(self, screen):
        if self.is_hammer: self.image = self.images[1]
        screen.blit(self.image, self.rect)

    '''重置'''

    def reset(self):
        self.image = self.images[0]
        self.is_hammer = False

class Hammer(pygame.sprite.Sprite):
    def __init__(self, images, position, **kwargs):
        pygame.sprite.Sprite.__init__(self)
        self.images = images
        self.image = self.images[0]
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.images[1])
        self.rect.left, self.rect.top = position
        # 用于显示锤击时的特效
        self.hammer_count = 0
        self.hammer_last_time = 4
        self.is_hammering = False

        ##############################在Hammer类下，__init__()函数后面，添加下列新的功能函数###################

    '''设置位置'''

    def setPosition(self, pos):
        self.rect.centerx, self.rect.centery = pos

    '''设置hammering'''

    def setHammering(self):
        self.is_hammering = True

    '''显示在屏幕上'''

    def draw(self, screen):
        if self.is_hammering:
            self.image = self.images[1]
            self.hammer_count += 1
            if self.hammer_count > self.hammer_last_time:
                self.is_hammering = False
                self.hammer_count = 0
        else:
            self.image = self.images[0]
        screen.blit(self.image, self.rect)

def init (self,name,score):
    self.name = name    #实例属性
    self.score = score

def QuitGame(use_pygame=True):
    if use_pygame:
        pygame.quit()
    sys.exit()

class Config():
    # 根目录
    rootdir = os.path.split(os.path.abspath(__file__))[0]
    # FPS
    FPS = 100
    # 屏幕大小
    SCREENSIZE = (993, 477)
    # 标题
    TITLE = '打地鼠'
    # 游戏常量参数
    HOLE_POSITIONS = [(90, -20), (405, -20), (720, -20), (90, 140), (405, 140), (720, 140), (90, 290), (405, 290), (720, 290)]
    BROWN = (150, 75, 0)
    WHITE = (255, 255, 255)
    RED = (255, 0, 0)
    RECORD_PATH = os.path.join(rootdir, 'score.rec')
    # 背景音乐路径
    BGM_PATH = os.path.join(rootdir, 'resources/audios/bgm.mp3')
    # 游戏声音路径
    SOUND_PATHS_DICT = {
        'count_down': os.path.join(rootdir, 'resources/audios/count_down.wav'),
        'hammering': os.path.join(rootdir, 'resources/audios/hammering.wav'),
    }
    # 字体路径
    FONT_PATH = os.path.join(rootdir, 'resources/fonts/Gabriola.ttf')
    # 游戏图片路径
    IMAGE_PATHS_DICT = {
        'hammer': [os.path.join(rootdir, 'resources/images/hammer0.png'), os.path.join(rootdir, 'resources/images/hammer1.png')],
        'begin': [os.path.join(rootdir, 'resources/images/begin.png'), os.path.join(rootdir, 'resources/images/begin1.png')],
        'again': [os.path.join(rootdir, 'resources/images/again1.png'), os.path.join(rootdir, 'resources/images/again2.png')],
        'background': os.path.join(rootdir, 'resources/images/background.png'),
        'end': os.path.join(rootdir, 'resources/images/end.png'),
        'mole': [
            os.path.join(rootdir, 'resources/images/mole_1.png'), os.path.join(rootdir, 'resources/images/mole_laugh1.png'),
            os.path.join(rootdir, 'resources/images/mole_laugh2.png'), os.path.join(rootdir, 'resources/images/mole_laugh3.png')
        ]
    }

def InitPygame(screensize, title='打地鼠', init_mixer=True):
    pygame.init()
    if init_mixer: pygame.mixer.init()
    screen = pygame.display.set_mode(screensize)
    pygame.display.set_caption(title)
    return screen


class WhacAMoleGame(PygameBaseGame):
    def __init__(self, **kwargs):
        self.cfg = Config
        super(WhacAMoleGame, self).__init__(config=self.cfg, **kwargs)
    '''运行游戏'''
    def run(self):
        # 初始化
        screen, resource_loader, cfg = self.screen, self.resource_loader, self.cfg
        while True:
            is_restart = self.GamingInterface(screen, resource_loader, cfg)
            if not is_restart:
                break



    ##############################在WhacAMoleGame类下，run()函数后面，添加下列新的功能函数###################
    '''游戏进行界面'''

    def GamingInterface(self, screen, resource_loader, cfg):
        # 播放背景音乐
        resource_loader.playbgm()
        audios = resource_loader.sounds
        # 加载字体
        font = pygame.font.Font(cfg.FONT_PATH, 40)
        # 加载背景图片
        bg_img = resource_loader.images['background']
        # 开始界面
        startInterface(screen, resource_loader.images['begin'])
        # 地鼠改变位置的计时
        hole_pos = random.choice(cfg.HOLE_POSITIONS)
        change_hole_event = pygame.USEREVENT
        pygame.time.set_timer(change_hole_event, 800)
        # 地鼠
        mole = Mole(resource_loader.images['mole'], hole_pos)
        # 锤子
        hammer = Hammer(resource_loader.images['hammer'], (500, 250))
        # 时钟
        clock = pygame.time.Clock()
        # 分数
        your_score = 0
        flag = False
        # 初始时间
        init_time = pygame.time.get_ticks()
        # 游戏主循环
        while True:
            # --游戏时间为60s
            time_remain = round((61000 - (pygame.time.get_ticks() - init_time)) / 1000.)
            # --游戏时间减少, 地鼠变位置速度变快
            if time_remain == 40 and not flag:
                hole_pos = random.choice(cfg.HOLE_POSITIONS)
                mole.reset()
                mole.setPosition(hole_pos)
                pygame.time.set_timer(change_hole_event, 650)
                flag = True
            elif time_remain == 20 and flag:
                hole_pos = random.choice(cfg.HOLE_POSITIONS)
                mole.reset()
                mole.setPosition(hole_pos)
                pygame.time.set_timer(change_hole_event, 500)
                flag = False
            # --倒计时音效
            if time_remain == 10:
                audios['count_down'].play()
            # --游戏结束
            if time_remain < 0: break
            count_down_text = font.render('Time: ' + str(time_remain), True, cfg.WHITE)
            # --按键检测
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    QuitGame()
                elif event.type == pygame.MOUSEMOTION:
                    hammer.setPosition(pygame.mouse.get_pos())
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1:
                        hammer.setHammering()
                elif event.type == change_hole_event:
                    hole_pos = random.choice(cfg.HOLE_POSITIONS)
                    mole.reset()
                    mole.setPosition(hole_pos)
            # --碰撞检测
            if hammer.is_hammering and not mole.is_hammer:
                is_hammer = pygame.sprite.collide_mask(hammer, mole)
                if is_hammer:
                    audios['hammering'].play()
                    mole.setBeHammered()
                    your_score += 10
            # --分数
            your_score_text = font.render('Score: ' + str(your_score), True, cfg.BROWN)
            # --绑定必要的游戏元素到屏幕(注意顺序)
            screen.blit(bg_img, (0, 0))
            screen.blit(count_down_text, (875, 8))
            screen.blit(your_score_text, (800, 430))
            mole.draw(screen)
            hammer.draw(screen)
            # --更新
            pygame.display.flip()
            clock.tick(60)
        # 读取最佳分数(try块避免第一次游戏无.rec文件)
        try:
            best_score = int(open(cfg.RECORD_PATH).read())
        except:
            best_score = 0
        # 若当前分数大于最佳分数则更新最佳分数
        if your_score > best_score:
            f = open(cfg.RECORD_PATH, 'w')
            f.write(str(your_score))
            f.close()
        # 结束界面
        score_info = {'your_score': your_score, 'best_score': best_score}
        is_restart = endInterface(screen, resource_loader.images['end'], resource_loader.images['again'], score_info,
                                  cfg.FONT_PATH, [cfg.WHITE, cfg.RED], cfg.SCREENSIZE)
        return is_restart

if __name__ == '__main__':
    Game = WhacAMoleGame()
    Game .run()