celeste.py

Created by loic-azavant

Created on May 12, 2023

19.1 KB

“Celeste” - Jeu original développé par Maddy Makes Games Réutilisation des idées, textures et logo avec l’autorisation de Maddy Makes Games.

Allez voir le jeu original, il est vraiment trop bien: https://www.celestegame.com/

Il y a besoin de celeste_levels.py


from kandinsky import *
from time import *
from ion import *

import micropython as mp
mp.kbd_intr(KEY_HOME)
  
def load_level(n):
  f=open("celeste_levels.py","r")
  for i in range(n+1):
    while f.readline()!="\n": pass
  levels = ["("]
  a=""
  while a!="\n":
    a=f.readline()
    levels.append(a.replace("\x00","").replace("\n",""))
  f.close()
  levels.pop(-1)
  levels.append(")")
  return "".join(levels)
  


def draw_image(rle,x0,y0,w,pal,zoomx,zoomy,itransp=-1):
  i,x = 0,0
  x0,y0=int(x0),int(y0)
  nvals = len(pal) -1
  nbits = 0
  while nvals:
    nvals>>=1
    nbits+=1
  maskval = (1<<nbits)-1
  maskcnt = (0xFF>>nbits>>1)<<nbits
  while i<len(rle):
    v=rle[i]
    mv=v&maskval
    c=(v&maskcnt) >> nbits
    if v&0b10000000 or nbits==8:
      i+=1
      c|=rle[i]<<(7-nbits+(nbits==8))
    c+=1
    while c:
      cw=min(c,w-x)
      if mv!=itransp:
        fill_rect(x0+x*zoomx,y0,cw*zoomx,zoomy,pal[mv])
      c-=cw
      x=(x+cw)%w
      y0 += x==0 and zoomy
    i+=1

palette = ("#f8f0e0","#20b0f8",(0,0,100),"#00e850","#f80048","#645450","#c0c0c0","#f8a000","#a85030")

images = (None,
          None,
          b"\2\xa0\x021 A\20Q\20\21\20\21\20\21\20\21\20Q",
          b"\xf0\1\1P1\20\xa1\4",
          b"`\2\xf0\x0010A Q\20!\0\21\20Q\20Q\20",
          b"\20Q\20Q\20\21\0!\20Q A01\xf0\0\2`",
          b"\xa1\4\x101P\1\xf0\1",
          b"Q\20Q\20\1\20!\20\1\20!\20A 1\xa0\2\2",
          b"\20Q A A0101 A A\20Q",
          b"\xf1\a",
          b"Q\20A A 1010A A Q\20",
          b"\2\xa0\2!0A A0!\x80\1\2`",
          b"\x80\3\x01010\1\x80\3",
          b"`\2\x80\1!0A A0!\xa0\2\2",
          b"\2P\2\xa0\2\21@1010\1\0\x1101\20",
          b" !@\21P\21@!@101@\21 \2P\2",
          b"\x101@!@!0!@!P\21P\21@1\20",
          b"\2P\2\xa0\1\1P101@\21\xa0\1\2P\2",
          b'\x82\1\b7\b"\5\22\5B\25B\5\22\5B\25B\5\22\5B\25',
          b"\x95\x022525252\x95\2",
          b'\22\0"\0"\0"\0"\0"\0\22\6\20\2\6\20\2\6\0\6\2\6\0\6\5\6\0\6\5\6\0\6\5\6\0\6\5\6\0\6\5&\5&',
          b'%B6\0"\6`F"%B6\0"\6`F"',
          b'&\5&\5\6\0\6\5\6\0\6\5\6\0\6\5\6\0\6\5\6\0\6\2\6\0\6\2\6\20\2\6\20\22\0"\0"\0"\0"\0"\0\22',
          b'"F`\6"\x006B%"F`\6"\x006B%',
          b'\22\3\2\23B\23B4"\4\a4\x124\a\4\22\24\a$"4B\24"')


palette2 = (
"k","#b8c0c0","#787898","#10acf8",
)

logo = (
b"l\1\xd0\1\1\0\1\xc8\1\2\b\1\xc0\1\2\20\3\xb8\1\2\24\3\xb4\1\3\30\3\b\1\xa0\1\2 \3\0\2\0\2\x98\1\3(\2\b\2\x94\1\3\x84\1\21\4\25\4\5\4\3\b\25\b\21\4\25\4\25\0\31\0\31\0\5\0\3\f\31\0\31\0\31\0!\b\5\0\5\24\5\24\5\24\5\34\5\f\5\20\6\24\16\f\6\24\16\f\32\b\6\f\16\b\6\b\6\0\6\24\6\f\2\0\6,\2\b\6\f\6\20\32\0\26\4\32\0\26\4\32\b\6\f\26\4\22\4\32\0\32\0\32\4\22\f\6\f\32\x8c\2\3t\3T\3\xfc\0\3H\a\x84\1\3@\3\x8c\1\3<\3\x90\1\38\3\x94\1\3\4\a$\3\x98\1\3\0\3\0\3 \3\xa0\1\3\b\3\34\3\xb0\1\3\34\3\xb4\1\3\24\3\xbc\1\3\f\3\xc4\1\3"
)

def drw_logo():
  draw_image(logo, 105, 20, 55, palette2, zoomx=2, zoomy=2, itransp=0)

def drw_texture(i,x,y):
  if i<2:
    fill_rect(x,y,16,16,(0,0,100))
  else:
    draw_image(images[i],x,y,8,palette,2,2)





keys = {}

def keyinput(key,value=None):
  if value!=None:
    keyPressed = value
  else:
    keyPressed = keydown(key)
  if not key in keys:
    keys[key] = False
  if keyPressed and not keys[key]:
    keys[key] = True
    return True
  if keys[key]:
    if not keyPressed:
      keys[key] = False
    return False
  return False


tile_colors = ((0,0,100),(220,150,50),(0,200,250),(255,0,0),(150,150,0))

class World:
  def __init__(self):
    self.chapter = 0
    self.x = 0
    self.y = 14
    self.dataX = 0
    self.dataY = 0
    self.dataHeight = 13
    self.dataWidth = 20
    self.nb_on_screen = (20,13)
    self.colors = tile_colors
    self.scale = 16
    self.deadTiles = {20,21,22,23}
    self.colliderTiles = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}
    self.spawnTile = 1
    self.trampoline = 18
    self.currentLevel = 8
    self.data = tuple()
    self.t = monotonic()


  def drw(self):
    fill_rect(0,14,320,208,tile_colors[0])
    for i in range(self.nb_on_screen[1]):
      for j in range(self.nb_on_screen[0]):
        drw_texture(ord(self.data[self.currentLevel][0][(self.dataY+i)*self.dataWidth+j+self.dataX])-95,self.x+j*self.scale,self.y+i*self.scale)
  
  def move(self,x,y):
    if 0>self.dataX+x or self.dataWidth-self.nb_on_screen[0]<self.dataX+x:
      x=0
    if 0>self.dataY+y or self.dataHeight-self.nb_on_screen[1]<self.dataY+y:
      y=0
    if x==0 and y==0:
      return
    for ghost in ghosts:
      fill_rect(ghost.x,ghost.y,ghost.sx,ghost.sy,tile_colors[0])
      ghost.x -= x*self.scale
      ghost.y -= y*self.scale
    on_screen_before = [self.data[self.currentLevel][0][(self.dataY+i)*self.dataWidth+self.dataX:(self.dataY+i)*self.dataWidth+self.dataX+self.nb_on_screen[0]] for i in range(self.nb_on_screen[1])]
    self.dataX += x
    self.dataY += y
    on_screen_now = [self.data[self.currentLevel][0][(self.dataY+i)*self.dataWidth+self.dataX:(self.dataY+i)*self.dataWidth+self.dataX+self.nb_on_screen[0]] for i in range(self.nb_on_screen[1])]
    for i in range(self.nb_on_screen[1]):
      for j in range(self.nb_on_screen[0]):
        if on_screen_before[i][j] != on_screen_now[i][j]:
          drw_texture(ord(on_screen_now[i][j])-95,self.x+j*self.scale,self.y+i*self.scale)
    player.y -= y*self.scale
    player.x -= x*self.scale
    
  def getMapTileAtPoint(self,x,y):
    return (int((x+self.dataX*self.scale-self.x)/self.scale),
            int((y+self.dataY*self.scale-self.y)/self.scale))

  def getMapTilePosition(self,x,y):
    return ((x-self.dataX)*self.scale+self.x,
            (y-self.dataY)*self.scale+self.y)
  
  def getTile(self,x,y):
    if x<0 or x>=self.dataWidth or y<0 or y>=self.dataHeight:
      return 0
    return ord(self.data[self.currentLevel][0][y*self.dataWidth+x])-95

ghosts = []

class Ghost:
  def __init__(self):
    ghosts.append(self)
    self.x = round(player.x)
    self.y = round(player.y)
    self.sx = player.sx
    self.sy = player.sy
    self.c = player.ori_col
    self.max_step = 50
    self.step = 0
  
  def drw(self):
    fill_rect(self.x,self.y,self.sx,self.sy,self.c)
    if self.step == self.max_step:
      ghosts.remove(self)
      del self
      return
    self.step += 1
    self.c = ((self.c[0]*(self.max_step-self.step)+tile_colors[0][0]*self.step)//self.max_step,
              (self.c[1]*(self.max_step-self.step)+tile_colors[0][1]*self.step)//self.max_step,
              (self.c[2]*(self.max_step-self.step)+tile_colors[0][2]*self.step)//self.max_step,)
    


class Player:
  def __init__(self):
    self.sx = 8
    self.sy = 14
    self.vx = 0
    self.vy = 0
    self.ori_col = (0,200,0)
    self.col = (0,200,0)
    
    self.nb_dash = 1
    self.max_nb_dash = 1
    self.dashing = False
    self.dash_time = 0
    self.max_dash_time = 16
    self.dash_speed = 4
    self.diagonal_dash = False
    
    self.on_ground = False
    self.on_right_wall = False
    self.on_left_wall = False
    
    self.jumping_trampoline = False

    self.climbing = False
    self.climbing_time = 0
    self.max_climbing_time = 1000
    self.climbing_speed = 0.04
    self.max_climbing_speed = 0.8
    self.climbing_delay = -1
    
    self.tuto_can_dash = False
    self.tuto_can_climb = False
    self.tuto_can_walljump = False
    
    self.last_level_jump = True
    
    self.deathCounter = 0
    
    self.collision_preciseness = 1
  
  def init(self):
    if world.currentLevel == 0:
      self.tuto_can_climb = False
      self.tuto_can_dash = False
    ghosts.clear()
    world.dataHeight,world.dataWidth = world.data[world.currentLevel][1]
    a=world.data[world.currentLevel][0].find("`")
    x2,y2 = a%world.dataWidth,a//world.dataWidth

    world.dataX = max(0,min(world.dataWidth-world.nb_on_screen[0],x2-world.nb_on_screen[0]//2))
    world.dataY = max(0,min(world.dataHeight-world.nb_on_screen[1],y2-world.nb_on_screen[1]//2))

    self.x,self.y = world.getMapTilePosition(x2,y2)
    self.old_x,self.old_y = self.x,self.y
    self.vx,self.vy = 0,0
    self.dashing = False
    self.dash_time = 0
    world.drw()

  def drw(self,x,y,c):
    fill_rect(round(x),round(y),self.sx,self.sy,c)

  def check_level_one(self):
    if world.chapter!=0:
      return
    if self.x+world.dataX*world.scale<50 and world.currentLevel==0:
      self.climbing=False
      if not self.tuto_can_climb:
        draw_string("Climb with [back]",75,20,"w",tile_colors[0])
        self.tuto_can_climb = True
      if 170>world.dataX*world.scale+self.x>154:
        draw_string("Climb with [back]",75,20,tile_colors[0],tile_colors[0])
    if world.currentLevel==1:
      if not self.tuto_can_walljump:
        draw_string("Walljump by pressing\n[OK] against a wall",3,17,"w",tile_colors[0],1)
        self.tuto_can_walljump = True
    if world.dataX*world.scale+self.x<448 or world.currentLevel!=3:
      self.nb_dash = 0
    else:
      if not self.tuto_can_dash:
        for i in range(10):
          self.old_x,self.old_y = self.x,self.y
          world.move(1,0)
          self.drw(self.old_x,self.old_y,tile_colors[0])
          self.drw(self.x,self.y,"g")
          sleep(0.05)
        for i in range(18):
          draw_string("Dash with [on/off]"[i],70+i*10,20,"w",tile_colors[0])
          sleep(0.08)
        while not keydown(KEY_ONOFF): pass
        draw_string("Dash with [on/off]",70,20,tile_colors[0],tile_colors[0])
        self.jumping_trampoline = False
        self.dashing = True
        dashed = True
        self.vx = self.dash_speed
        self.vy = -self.dash_speed
        self.diagonal_dash=True
        self.tuto_can_dash = True
      else:
        self.vx=0.4
      if self.on_ground:
        sleep(0.5)
        for i in range(21):
          fill_rect(0,14+i*10,320,10,(100,0,75))
          sleep(0.05)
        sleep(0.2)
        for i in range(15):
          draw_string("You can do this"[i],85+i*10,100,"w",(100,0,75))
          sleep(0.15)
        sleep(1.5)
        for i in range(9):
          draw_string("Good luck"[i],115+i*10,130,"w",(100,0,75))
          sleep(0.1)
        sleep(1)
        for i in range(10):
          draw_string("Press [OK]"[i],110+i*10,190,"w",(100,0,75))
          sleep(0.1)
        while not keydown(4): pass
        home_menu()


  def onGround(self):
    bottomLeft = world.getMapTileAtPoint(self.x+self.collision_preciseness,self.y+self.sy)
    bottomLeftTile = world.getTile(*bottomLeft)
    bottomRight = world.getMapTileAtPoint(self.x+self.sx-self.collision_preciseness,self.y+self.sy)
    bottomRightTile = world.getTile(*bottomRight)
    if bottomLeftTile in world.colliderTiles or bottomRightTile in world.colliderTiles:
      if bottomLeftTile == world.trampoline or bottomRightTile == world.trampoline:
        self.vy = -1.7
        self.jumping_trampoline = True
      else:
        self.vy = min(0,self.vy)
      self.y = world.getMapTilePosition(*bottomRight)[1]-self.sy
      return True
    elif bottomLeftTile in world.deadTiles or bottomRightTile in world.deadTiles:
      self.playDead()
    return False

  def onCeiling(self):
    topLeft = world.getMapTileAtPoint(self.x+self.collision_preciseness,self.y)
    topLeftTile = world.getTile(*topLeft)
    topRight = world.getMapTileAtPoint(self.x+self.sx-self.collision_preciseness,self.y)
    topRightTile = world.getTile(*topRight)
    if topLeftTile in world.colliderTiles or topRightTile in world.colliderTiles:
      self.vy = max(0,self.vy)
      self.y = world.getMapTilePosition(*topLeft)[1]+world.scale
      return True
    elif topLeftTile in world.deadTiles or topRightTile in world.deadTiles:
      self.playDead()
    return False
      
  def onRightWall(self):
    topRight = world.getMapTileAtPoint(self.x+self.sx,self.y+self.collision_preciseness)
    topRightTile = world.getTile(*topRight)
    bottomRight = world.getMapTileAtPoint(self.x+self.sx,self.y+self.sy-self.collision_preciseness)
    bottomRightTile = world.getTile(*bottomRight)
    if topRightTile in world.colliderTiles or bottomRightTile in world.colliderTiles:
      self.vx = min(0,self.vx)
      self.x = world.getMapTilePosition(*topRight)[0]-self.sx
      return True
    if topRightTile in world.deadTiles or topRightTile in world.deadTiles:
      self.playDead()
    return False
    
  def onLeftWall(self):
    topLeft = world.getMapTileAtPoint(self.x,self.y+self.collision_preciseness)
    topLeftTile = world.getTile(*topLeft)
    bottomLeft = world.getMapTileAtPoint(self.x,self.y+self.sy-self.collision_preciseness)
    bottomLeftTile = world.getTile(*bottomLeft)
    if topLeftTile in world.colliderTiles or bottomLeftTile in world.colliderTiles:
      self.vx = max(0,self.vx)
      self.x = world.getMapTilePosition(*topLeft)[0]+world.scale
      return True
    if topLeftTile in world.deadTiles or bottomLeftTile in world.deadTiles:
      self.playDead()
    return False

  def fixCollisions(self):
    self.on_ground = self.onGround()
    self.on_right_wall = self.onRightWall()
    self.on_left_wall = self.onLeftWall()
    self.onCeiling()

  def playDead(self):
    self.init()
    self.deathCounter += 1

  def updt(self):
    gravity = 0.02
    
    self.old_x,self.old_y = self.x,self.y
    
    pressedKeys = get_keys()
    arrowLeft = "left" in pressedKeys
    arrowRight = "right" in pressedKeys
    arrowUp = "up" in pressedKeys
    arrowDown = "down" in pressedKeys
    okPressed = "OK" in pressedKeys
    homePressed = "back" in pressedKeys
    onOffPressed = "onOff" in pressedKeys
    
    if world.chapter!=0 and world.currentLevel==len(world.data)-1:
      if self.last_level_jump:
        sleep(0.2)
        self.vy=-1.3
        self.last_level_jump = False
      self.vx=0.7
      okPressed = True
      if self.on_ground:
        for i in range(10):
          draw_string("You did it"[i],110+i*10,100,"w",tile_colors[0])
          sleep(0.14)
        sleep(0.5)
        for i in range(80):
          draw_string("You did it",110,100-i,"w",tile_colors[0])
          sleep(0.02)
        a="You died "+str(self.deathCounter)+" time"+"s"*(self.deathCounter>1)
        for i in range(len(a)):
          draw_string(a[i],160-(len(a)*10)//2+i*10,100,"w",tile_colors[0])
          sleep(0.05)
        sleep(0.5)
        for i in range(50):
          draw_string(a,160-(len(a)*10)//2,100-i,"w",tile_colors[0])
          sleep(0.02)
        sleep(0.3)
        t=monotonic()-world.t
        a="It took you "+str(int(t//60))+" min and "+str(round(t)%60)+" sec"
        for i in range(len(a)):
          draw_string(a[i],160-(len(a)*10)//2+i*10,100,"w",tile_colors[0])
          sleep(0.05)
        sleep(0.3)
        for i in range(20):
          draw_string(a,160-(len(a)*10)//2,100-i,"w",tile_colors[0])
          sleep(0.02)
        sleep(0.3)
        for i in range(8):
          draw_string("Congrats"[i],120+i*10,110,"w",tile_colors[0])
          sleep(0.05)
        sleep(0.3)
        for i in range(10):
          draw_string("Press [OK]"[i],110+i*10,140,"w",tile_colors[0])
          sleep(0.02)
        while not keydown(4): pass
        home_menu()
    
    
    if not self.dashing:
      arrowHori = arrowRight-arrowLeft
      if arrowHori != 0:
        if self.on_ground:
          self.vx += 0.04*arrowHori
        else:
          self.vx += 0.015*arrowHori
      else:
        if self.on_ground:
          self.vx /= 1.5
        else:
          self.vx /= 1.1
    
      self.vx = max(-0.8,min(0.8,self.vx))
    
      if okPressed:
        if self.on_ground:
          self.vy = -1.3
        elif self.on_left_wall or self.on_right_wall:
          self.vy = -0.8
          self.vx = 0.8*(self.on_left_wall-self.on_right_wall)
      elif self.vy < 0 and not self.jumping_trampoline:
        self.vy = max(-0.5,self.vy)
    
      if self.jumping_trampoline and self.vy > 0:
        self.jumping_trampoline = False
    
      if not self.climbing:
        self.vy += gravity
        self.vy = min(8,self.vy,0.8+50*((not self.on_left_wall and not self.on_right_wall) or self.climbing_time>self.max_climbing_time))

      if homePressed and (self.on_left_wall or self.on_right_wall):
        if self.climbing_time<self.max_climbing_time:
          if not self.climbing:
            self.vy = 0
          self.climbing = True
      else:
        self.climbing = False
      
      self.check_level_one()
      
      if self.climbing:
        arrowVerti = arrowDown-arrowUp
        if arrowVerti == 0:
          self.vy /= 2
        else:
          self.vy += self.climbing_speed*arrowVerti
          self.vy = max(-self.max_climbing_speed,min(self.max_climbing_speed,self.vy))
        self.climbing_time += 1+abs(arrowVerti)*3
        if self.climbing_time > self.max_climbing_time:
          self.climbing = False
          if self.climbing_delay==-1:
            self.climbing_delay = 0
        
      dashed = False
      if keyinput(KEY_ONOFF,onOffPressed) and self.nb_dash>0:
        self.jumping_trampoline = False
        self.nb_dash -= 1
        self.dashing = True
        dashed = True
        self.vx = self.dash_speed*(arrowHori+(not arrowDown and not arrowUp and not arrowLeft and not arrowRight))
        self.vy = self.dash_speed*(arrowDown-arrowUp)
        if self.vx != 0 and self.vy != 0:
          self.diagonal_dash = True
      
      if self.on_ground:
        if not dashed:
          self.nb_dash = self.max_nb_dash
        self.climbing_time = 0
        self.col = self.ori_col
        self.climbing_delay = -1
        
      if self.climbing_delay%20==0:
        if self.climbing_delay!=-1:
          self.climbing_speed += 1
        if self.col == self.ori_col:
          self.col = "r"
        else:
          self.col = self.ori_col

    else:
      self.dash_time += 1+0.5*self.diagonal_dash
      if self.dash_time%(4-self.diagonal_dash)==0:
        Ghost()
      if self.dash_time > self.max_dash_time:
        self.dashing = False
        self.diagonal_dash = False
        self.dash_time = 0


    self.y += self.vy
    self.x += self.vx
 
    if (self.x<0 and world.data[world.currentLevel][-1]==0) or (self.y<0 and world.data[world.currentLevel][-1]==1) or (self.x>320 and world.data[world.currentLevel][-1]==2) or (self.y>222 and world.data[world.currentLevel][-1]==3):
      world.currentLevel += 1
      self.init()
    elif self.y<0 or self.y>222 or self.x<0 or self.x>320:
      self.deathCounter += 1
      self.init()        
    
    self.fixCollisions()
    
    self.drw(self.old_x,self.old_y,tile_colors[0])
    will_move = [int((self.x>270)-(self.x<40)),int((self.y>180)-(self.y<40))]
    if will_move != [0,0]:
      world.move(*will_move)
    self.drw(self.x,self.y,self.col)

world = World()
player = Player()

def home_menu():
  fill_rect(0,0,320,222,tile_colors[0])
  drw_logo()
  draw_string("[0] Prologue",20,115,"w",tile_colors[0])
  draw_string("[1] Chapter 1 - A side",20,135,"w",tile_colors[0])
  draw_string("[2] Chapter 1 - B side",20,155,"w",tile_colors[0])
  draw_string("[3] Chapter 1 - C side",20,175,"w",tile_colors[0])
  if len(eval(load_level(4)))!=1:
    draw_string("[4] Chapter 1 - Custom",20,195,"w",tile_colors[0])
  a=[KEY_ZERO,KEY_ONE,KEY_TWO,KEY_THREE,KEY_FOUR]
  while 1:
    for i in range(5):
      if keydown(a[i]):
        world.chapter = i
        world.currentLevel = 0
        world.data = eval(load_level(i))
        world.t = monotonic()
        player.deathCounter = 0
        player.init()
        return

home_menu()

while 1:
  fill_rect(0,0,320,14,(248,180,48))
  player.updt()
  for i in ghosts:
    i.drw()

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.