from xdf import *


keys = {}
def keydown(key, keyCode):
    keys[str(key)] = True
    #print(key, 'down')
def keyup(key, keyCode):
    key = str(key)
    if key in keys:
        keys[key] = False
        #print(key, 'up')

def isDown(key):
    return (key in keys) and keys[key]

class Sprite:
    def __init__(self, dis):
        self.id = 0
        self.x = 0
        self.y = 0
        self.w = 1
        self.h = 1
        self.visible = True
        self.dis = dis
    
    def front(self):
        self.dis.front()
        
    def move(self, x, y):
        self.x = x
        self.y = y
    
    def change(self, value):
        self.dis.change(value)
    
    def update(self, pVisible):
        self.dis.move(self.x, self.y)
        if pVisible and self.visible:
            self.dis.show()
        else:
            self.dis.hide()


class SpriteSheet(Sprite):
    def __init__(self, arr, config):
        self.isPlaying = True
        self.isFlag = False
        self.currentFrame = 0
        self.totalFrames = len(arr)
        self.stamps = []
        self.config = config
        self.flipped = False
        self.flipArr = []
        for i in range(len(arr)):
            sp = stamp(arr[i])
            self.stamps.append(sp)
            sp.hide()
            self.flipArr.append(False)
        Sprite.__init__(self, self.stamps[0])
    
    def play(self):
        self.isPlaying = True
    
    def stop(self):
        self.isPlaying = False
    
    def gotoAndStop(self, n):
        self.currentFrame = n;
        self.stop()
    
    def gotoAndPlay(self, n):
        self.currentFrame = n;
        self.play()
    
    def nextFrame(self):
        self.currentFrame = self.currentFrame + 1
        if self.isFlag:
            if self.currentFrame > self.flagEnd:
                self.currentFrame = self.flagStart
        elif self.currentFrame >= self.totalFrames:
            self.currentFrame = 0
    
    def playFlag(self, flag):
        if flag in self.config:
            a = self.config[flag]
            self.flagStart = a[0]
            self.flagEnd = a[1]
            self.isFlag = True
            self.gotoAndPlay(self.flagStart);
    
    def clearPlayFlag(self):
        self.isFlag = False
    
    def update(self, pVisible = True):
        if self.isPlaying:
            self.nextFrame()    
        for i in range(len(self.stamps)):
            self.stamps[i].hide()
        self.dis = self.stamps[self.currentFrame]
        if self.flipped != self.flipArr[self.currentFrame]:
            self.flipArr[self.currentFrame] = self.flipped
            self.dis.flip()
        super().update(pVisible)


class Container:
    def __init__(self):
        self.children = []
        self.visible = True
        
    def show(self):
        self.visible = True 
    
    def hide(self):
        self.visible = False
    
    def update(self, pVisible = True):
        for i in range(len(self.children)):
            self.children[i].update(pVisible and self.visible)

class HMenuLayer(Container):
    def __init__(self):
        Container.__init__(self)
        self.bg = Sprite(stamp('menuBg.jpg'))
        self.gamename = Sprite(stamp('gameName.png'))
        self.shuoming = Sprite(stamp('shuoming.png'))
        self.btn = Sprite(stamp('start0.png'))
        self.bg.move(384, 512)
        self.gamename.move(184, 312)
        self.shuoming.move(380, 700)
        self.btn.move(380, 860)
        self.btn.dis.tap = self.onStart
        self.children = [self.bg, self.gamename, self.shuoming, self.btn]
    
    def onStart(self):
        gameStart()
    
class HOverlayer(Container):
    def __init__(self):
        Container.__init__(self)
        self.bg = Sprite(stamp('overBg.png'))
        self.scoreBg = Sprite(stamp('pannelBg.png'))
        self.tfCj = Sprite(text('成绩', 384, 420, 20, '#00FF66', 'center'))
        self.tfFloor = Sprite(text('00', 384, 500, 30, '#FF9900', 'center'))
        self.tfScore = Sprite(text('00', 384, 540, 30, '#FF6666', 'center'))
        self.btn = Sprite(stamp('reStart0.png'))
        self.bg.move(384, 512)
        self.scoreBg.move(384, 512)
        self.tfCj.move(384, 420)
        self.tfFloor.move(384, 500)
        self.tfScore.move(384, 540)
        self.btn.move(384, 595)
        self.btn.dis.tap = self.onRestart
        self.children = [self.bg, self.scoreBg, self.tfCj, self.tfFloor, self.tfScore, self.btn]
        self.setScore(0,0)
        
    def setScore(self, floor, score):
        self.tfFloor.change('层数：' + str(floor))
        self.tfScore.change('分数：' + str(score))
    
    def onRestart(self):
        gameRestart()

class HLadderLayer(Container):
    def __init__(self):
        Container.__init__(self)
        self.pool = []
        self.reset()
        self.id = 1

    def update(self, pVisible = True):
        super().update(pVisible)
        for i in range(len(self.pool)):
            self.pool[i].visible = False
            self.pool[i].update(pVisible)
    
    def setYoffset(self, value):
        for i in range(len(self.children)):
            lad = self.children[i]
            lad.y += value
        while self.children[0].y > 1024 + 25:
            lad = self.children.pop(0)
            self.pool.append(lad)
        while self.children[-1].y > 0:
            self.addLadder(self.children[-1].y - 100)
    
    def reset(self):
        self.id = 1
        while len(self.children):
            self.pool.append(self.children.pop())
        for i in range(9):
            self.addLadder(1024 - 200 -i*100)
    
    def getLadders(self):
        return self.children
        
    def createLadder(self):
        if len(self.pool):
            return self.pool.pop()
        lad = Sprite(stamp('tizi.png'))
        lad.w = 220
        lad.h = 20
        return lad
        
    def addLadder(self, y):
        lad = self.createLadder()
        lad.x = random(80, 688)
        lad.y = y
        lad.visible = True
        self.children.append(lad)
        lad.id = self.id
        self.id += 1
    
class HBgLayer(Container):
    def __init__(self):
        Container.__init__(self)
        self.bg = Sprite(stamp('bg.jpg'))
        self.ground = Sprite(stamp('ground.png'))
        self.bg.x = 384
        self.ground.x = 384
        self.ground.w = 480
        self.ground.h = 24
        self.qiangArr = []
        self.children = [self.bg, self.ground]
        for i in range(3):
            qiangR = Sprite(stamp('qiangL.png'))
            qiangL = Sprite(stamp('qiangR.png'))
            self.qiangArr.append(qiangL)
            self.qiangArr.append(qiangR)
            qiangL.x = 80
            qiangR.x = 688
            self.children.append(qiangL)
            self.children.append(qiangR)
            
        self.reset()
    
    def update(self, pVisible = True):
        super().update(pVisible)
        for i in range(len(self.qiangArr)):
            self.qiangArr[i].front()
        
    def reset(self):
        self.bg.y = -176
        self.ground.visible = True
        self.ground.y = 1012
        for i in range(3):
            qiangL = self.qiangArr[i * 2]
            qiangR = self.qiangArr[i * 2 + 1]
            qiangL.y = 744 - 560*i
            qiangR.y = 744 - 560*i
        
    def setYoffset(self, value):
        for i in range(3):
            qy = self.qiangArr[2*i].y
            if qy > 744:
                qy -= 560 * 3
            self.qiangArr[2*i].y = self.qiangArr[2*i + 1].y = qy
        if self.ground.visible:
            self.ground.y += value
            if self.ground.y > 1024+200+25:
                self.ground.visible = False
        self.bg.y += value * 0.2
        if self.bg.y > 1200:
            self.bg.y -= 800
    
    def getGround(self):
        return self.ground

class Hero(Container):
    def __init__(self):
        Container.__init__(self)
        self.w = 40 #156
        self.h = 100 #112
        self.sp = SpriteSheet(
            ['hero1.png', 'hero2.png', 'hero3.png', 'hero4.png', 'hero5.png', 'hero6.png', 'hero7.png', 'hero8.png', 'hero9.png', 'hero10.png', 'hero11.png', 'hero12.png', 'hero13.png', 'hero14.png', 'hero15.png', 'hero16.png', 'hero17.png', 'hero18.png', 'hero19.png', 'hero20.png'],
            {
                'stand': [0, 4],
                'run': [5, 10],
                'jump': [11, 13],
                'jump2': [14, 19]
            }
        )
        self.x = 0
        self.y = 0
        self.speedX = 0
        self.speedY = 0
        self.inSpace = False
        self.state = ""
        self.MAX_SPEED = 20
        self.AX = 2
        self.FY = -25
        self.children = [self.sp]
        self.reset()
    
    def update(self, pVisible=True):
        self.sp.x = self.x
        self.sp.y = self.y
        super().update(pVisible)
        
    
    def setScale(self, sc):
        if sc < 0:
            self.sp.flipped = True
        else:
            self.sp.flipped = False
        
    def runLeft(self):
        if not self.inSpace:
            self.run()
        self.setScale(-1)
        self.speedX -= self.AX
        if self.speedX < -self.MAX_SPEED:
            self.speedX = -self.MAX_SPEED
    
    def runRight(self):
        if not self.inSpace:
            self.run()
        self.setScale(1)
        self.speedX += self.AX
        if self.speedX > self.MAX_SPEED:
            self.speedX = self.MAX_SPEED
    
    def jumpUp(self):
        if not self.inSpace:
            self.speedY = self.FY - abs(self.speedX / self.MAX_SPEED)*35
            if self.speedY < -40:
                self.jump2()
            else:
                self.jump()
    
    def jump(self):
        if self.changeState('jump'):
            self.inSpace = True
            
    def jump2(self):
        if self.changeState('jump2'):
            self.inSpace = True
            
    def run(self):
        self.changeState('run')
    
    def stand(self):
        self.changeState('stand')
    
    def changeState(self, state):
        if self.state != state:
            self.sp.playFlag(state)
            self.state = state
            return True
        return False
    
    def reset(self):
        self.speedX = 0
        self.speedY = 0
        self.inSpace = False
        self.stand()
        self.x = 384
        self.y = 1024-56
    
    def hitTest(self, sp):
        rect0 = Rect(self.x, self.y, self.w, self.h)
        rect1 = Rect(sp.x, sp.y, sp.w, sp.h)
        flag = not (rect1.left > rect0.right 
                    or rect1.right < rect0.left
                    or rect1.top > rect0.bottom
                    or rect1.bottom < rect0.top
                    ) and self.speedY > 0 and self.y < sp.y
        if flag:
            self.y = sp.y - sp.h / 2 - self.h / 2
            self.speedY = 0
        return flag

class Rect:
    def __init__(self, x, y, w, h):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.left = x - w / 2
        self.top = y - h / 2
        self.right = x + w / 2
        self.bottom = y + h /2

class FlyText(Sprite):
    def __init__(self, tf):
        Sprite.__init__(self, tf)
        self.life = 0
        self.step = 0
        
    def flyText(self, text, life, x, y, dy):
        self.change(text)
        self.life = life
        self.x = x
        self.y = y
        self.step = dy / life
        
    def update(self, pVisible):
        self.life = max(0, self.life-1)
        self.visible = self.life > 0
        if self.life > 0:
            self.y += self.step
        super().update(pVisible)
        self.front()
        

class HGameLayer(Container):
    def __init__(self):
        Container.__init__(self)
        self.autoMove = False
        self.score = 0
        self.floor = 0
        self.isRunning = False
        self.bgLayer = HBgLayer()
        self.ladderLayer = HLadderLayer()
        self.hero = Hero()
        self.tfFloor = Sprite(text('000', 0, 0, 40, '#FF6600'))
        self.tfScore = Sprite(text('000', 0, 0, 40, '#FF6600'))
        self.tfFloor.move(10, 50)
        self.tfScore.move(10, 100)
        self.flyTf = FlyText(text('000', 0, 0, 50, '#FF0000'))
        
        self.ladderLayer.setYoffset(0)
        self.children = [self.bgLayer, self.ladderLayer, self.hero, self.tfFloor, self.tfScore, self.flyTf]
    
    def setScore(self, floor, score):
        self.tfFloor.change('层数:' + str(floor))
        self.tfScore.change('分数:' + str(score))
        
    def update(self, pVisible = True):
        super().update(pVisible)
        self.tfFloor.front()
        self.tfScore.front()
        self.setScore(self.floor, self.score)
        #print('is run', self.isRunning)
        if not self.isRunning:
            return
        hero = self.hero
        if isDown('39') or yaogan.isRight():
            hero.runRight()
        elif isDown('37') or yaogan.isLeft():
            hero.runLeft()
        if isDown('38') or isDown('32') or yaogan.isSpace():
            hero.jumpUp()
        
        hero.x += hero.speedX
        hero.speedX *= 0.90
        if hero.x > 558:
            hero.x = 558
            hero.speedX *= 0.5
        elif hero.x < 210:
            hero.x = 210
            hero.speedX *= 0.5
        if abs(hero.speedX) < 0.5:
            hero.speedX = 0
        hero.speedY += 2
        hero.y += hero.speedY
        hero.inSpace = True
        fallFlag = hero.speedY > 2
        if hero.speedY > 0:
            # 检测地板
            # print('hit test floor')
            ground = self.bgLayer.getGround()
            if hero.hitTest(ground):
                hero.inSpace = False
                if fallFlag:
                    pass
            else:
                # 检测梯子
                ladders = self.ladderLayer.getLadders()
                for i in range(len(ladders)):
                    ladder = ladders[i]
                    # print('hit test ladder')
                    if hero.hitTest(ladder):
                        hero.inSpace = False
                        if fallFlag:
                            pass
                        ind = ladder.id
                        if self.floor < ind:
                            step = ind - self.floor
                            if step > 3:
                                self.score += 10
                                self.flyTf.flyText('连跳' + str(step) + '层', 40, hero.x, hero.y, -300)
                            self.floor = ind
                            self.score += step * 10
        if hero.y < 512:
            self.autoMove = True
        if self.autoMove:
            self.bgLayer.setYoffset(2)
            self.ladderLayer.setYoffset(2)
        if hero.y < 400:
            step = min(40, 400 - hero.y)
            hero.y += step
            self.bgLayer.setYoffset(step)
            self.ladderLayer.setYoffset(step)
        if not hero.inSpace:
            if hero.speedX:
                hero.run()
            else:
                hero.stand()
        if self.autoMove and hero.y > 1024:
            self.stop()
            self.gameOver()
    
    def gameOver(self):
        gameOver()
        
    def reset(self):
        self.bgLayer.reset()
        self.ladderLayer.reset()
        self.hero.reset()
        self.autoMove = False
        self.floor = 0
        self.score = 0
    
    def start(self):
        self.reset()
        self.isRunning = True
    
    def stop(self):
        self.isRunning = False
    
gameLayer = HGameLayer()
menuLayer = HMenuLayer()
overLayer = HOverlayer()
gameLayer.visible = False
overLayer.visible = False

def hideAll():
    gameLayer.visible = False
    overLayer.visible = False
    menuLayer.visible = False
    
def gameRestart():
    hideAll()
    menuLayer.visible = True
    yaogan.show()

def gameStart():
    hideAll()
    gameLayer.visible = True
    gameLayer.start()
    yaogan.show()
    
def gameOver():
    hideAll()
    overLayer.visible = True
    overLayer.setScore(gameLayer.floor, gameLayer.score)
    yaogan.hide()
    
def loop():
    menuLayer.update()
    overLayer.update()
    gameLayer.update()
    yaogan.front()

class Yaogan:
    def __init__(self):
        y0 = 1024-100
        x0 = 100
        x1 = 768-100
        self.x0 = x0
        self.y0 = y0
        self.x1 = x1
        self.c1 = circle(x0, y0, 100, 'transparent', 'blue')
        self.c2 = circle(x0, y0, 30, 'red')
        self.c3 = circle(x1, y0, 100, 'rgb(255, 0, 0, 0.5)')
        self.isDown1 = False
        self.isDown3 = False
        self.dp = [0, 0]
        self.visible = True
    
    def hide(self):
        self.c1.move(0, 1300)
        self.c2.move(0, 1300)
        self.c3.move(0, 1300)
    
    def show(self):
        self.c1.move(self.x0, self.y0)
        self.c2.move(self.x0, self.y0)
        self.c3.move(self.x1, self.y0)
    
    def front(self):
        self.c1.front()
        self.c2.front()
        self.c3.front()
    
    def isLeft(self):
        return self.dp[0] < -10
    
    def isRight(self):
        return self.dp[0] > 10
    
    def isSpace(self):
        return self.isDown3
    
    def touch(self, x, y):
        if self.c1.hits(x, y):
            self.isDown1 = True
        elif self.c3.hits(x, y):
            self.isDown3 = True
    def touching(self, x, y):
        if self.c1.hits(x, y):
            self.c2.move(x, y)
            self.dp = [x - self.x0, y - self.y0]
    
    def untouch(self, x, y):
        if self.c1.hits(x, y) or x <= 768 / 2:
            self.isDown1 = False
            self.c2.move(self.x0, self.y0)
            self.dp = [0, 0]
        elif self.c3.hits(x, y) or x > 768 / 2:
            self.isDown3 = False

yaogan = Yaogan()
yaogan.hide()

def touch():
    yaogan.touch(x, y)

def untouch():
    yaogan.untouch(x, y)

def touching():
    yaogan.touching(x, y)

import xdf
go(globals())