Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import pygame
- import lzma, base64
- import time, math, random
- # sprites in binary format then encoded using base64
- LADYBIRD_SPRITES = b'/Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4AVrAbtdAAJgHwVq0WshYoaIhrJyex2rbCiorbp1hmiVEMM\
- h2lfK0a+0RSr8hVfb8UoPnlrWmZoOIhZtMIBkm6tGF2WtskMU35LTD2URe5iSj8MGMHtpeeCH5nnHEaGaoPaPnEX9vIZm60OMaMW\
- aValnLDtYwEEF7IvWYkEPvvplJHYvTO7EPw7Eu5bpZ8oH48IceM4+WU0lNQq5tw2V536wabip3AT+CY0z5LZq488wBHttrO4AbPn\
- cDMRBeIfzXPg/TiJAwAVodAlcSttVgaL0IMlMiWHRQ2HeEwRV4hOyM7iNgr8G6XHS5/VJzEXwoL9IuBnv6avNzEL8ijb45a5hKNJ\
- HFKPbU2rZGGpPkvW+x1X5x/nDTDOh+4hoIYvydUZCL+Bl2W4jhWE4Xcu0MjQ3JbZ1SbM2Xpq0/UNxCMJHHprI6nkjMgmfaaARA9v\
- jE9J/1Bj1+4iCaieMIcOr4IPzY/WX5usP3vaVeYUQqzYrkWqm+Z1E/52y8R92LSTa1tw/8dUPrrNyAR1YVUXrHEKCVrVz0IDCRW1\
- GQKLc2xePO5u9NQoARPbAguqvhmCAdIrAQo2JSVA3Y/Fh6D0DPiAAAADA4oH4qKJ93AAB1wPsCgAAzPnwGrHEZ/sCAAAAAARZWg=\
- ='
- # this class recreates all of the sprites from the data above
- class spritesSheet:
- def b(s, a):
- return a[1 :], int.from_bytes(a[: 1], "big")
- def w(s, a):
- return a[2 :], int.from_bytes(a[: 2], "big")
- def d(s, a):
- return a[4 :], int.from_bytes(a[: 4], "big")
- def rgb(s, a):
- return a[3 :], int.from_bytes(a[: 3], "big")
- def name(s, a):
- sl = int.from_bytes(a[: 1], "big")
- return a[1 + sl :], a[1 : sl + 1].decode("utf-8")
- def cr(s, index):
- return [index % s.c * s.cw, int(index / s.c) * s.ch, s.cw, s.ch]
- def __init__(s, lz64_data):
- # decompress data
- data = lzma.decompress(base64.b64decode(lz64_data))
- # get image dimensions
- data, s.c = s.b(data) # cols
- data, s.r = s.b(data) # rows
- data, s.cw = s.b(data) # cell width
- data, s.ch = s.b(data) # cell height
- s.iw = s.c * s.cw # image width
- s.ih = s.r * s.ch # image height
- # get palette length
- data, s.pl = s.b(data) # palette length
- # get palette
- s.palette = []
- for index in range(s.pl):
- data, irgb = s.rgb(data)
- s.palette.append(irgb)
- # create pygame surface to place spritesheet
- s.surface = pygame.Surface((s.iw, s.ih))
- pa = pygame.PixelArray(s.surface)
- # get image data length in bytes
- idl = s.iw * s.ih
- # extract image data
- for index in range(idl):
- data, pi = s.b(data) # palette index
- pa[index % s.iw][int(index / s.iw)] = s.palette[pi]
- pa.close()
- del pa
- # make the sprites using the assembly data
- s.sprites = {}
- cell = pygame.Surface((s.cw, s.ch)) # to temp store cell
- while data:
- data, sn = s.name(data) # sprite name
- data, sw = s.w(data) # sprite width, if width is zero then it's a copy instruction
- if sw == 0: # copy instruction?
- data, snc = s.name(data) #sprite name to copy
- data, at = s.b(data) # assembly attribute
- # apply attribute 0 = none, 1 = h flip, 2 = v flip, 3 = rot 90, 4 = rot 180, 5 = rot 270
- if at == 0:
- s.sprites[sn] = s.sprites[snc].copy()
- elif at == 1:
- s.sprites[sn] = pygame.transform.flip(s.sprites[snc], True, False)
- elif at == 2:
- s.sprites[sn] = pygame.transform.flip(s.sprites[snc], False, True)
- elif at == 3:
- s.sprites[sn] = pygame.transform.rotate(s.sprites[snc], -90)
- elif at == 4:
- s.sprites[sn] = pygame.transform.rotate(s.sprites[snc], -180)
- elif at == 5:
- s.sprites[sn] = pygame.transform.rotate(s.sprites[snc], -270)
- continue
- data, sh = s.w(data) # sprite height
- sc = math.ceil(sw / s.cw) # sprite columns
- sr = math.ceil(sh / s.ch) # sprite rows
- scc = sc * sr # sprite cell count
- scc_index = 0
- # create a surface for the sprite
- s.sprites[sn] = pygame.Surface((sw, sh))
- # cycle through assembly instructions
- while scc_index < scc:
- data, ci = s.w(data) # cell index
- data, at = s.b(data) # assembly attribute
- if at < 6: # single cell placement?
- # calc x, y coords of cell placement
- x = scc_index % sc * s.cw
- y = int(scc_index / sc) * s.ch
- # get cell image
- cell.blit(s.surface, (0, 0), s.cr(ci))
- # apply attribute 0 = none, 1 = h flip, 2 = v flip, 3 = rot 90, 4 = rot 180, 5 = rot 270
- if at == 0:
- s.sprites[sn].blit(cell, (x, y))
- elif at == 1:
- s.sprites[sn].blit(pygame.transform.flip(cell, True, False), (x, y))
- elif at == 2:
- s.sprites[sn].blit(pygame.transform.flip(cell, False, True), (x, y))
- elif at == 3:
- s.sprites[sn].blit(pygame.transform.rotate(cell, -90), (x, y))
- elif at == 4:
- s.sprites[sn].blit(pygame.transform.rotate(cell, -180), (x, y))
- elif at == 5:
- s.sprites[sn].blit(pygame.transform.rotate(cell, -270), (x, y))
- scc_index += 1
- else:
- data, r = s.w(data) # get range count
- for index in range(r):
- # get x, y coords of cell placement
- x = (scc_index + index) % sc * s.cw
- y = int((scc_index + index) / sc) * s.ch
- # get cell image
- cell.blit(s.surface, (0, 0), s.cr(ci))
- # apply attribute 6 = none, 7 = h flip, 8 = v flip, 9 = rot 90, 10 = rot 180, 11 = rot 270
- if at == 6 or at == 12 or at == 18:
- s.sprites[sn].blit(cell, (x, y))
- elif at == 7 or at == 13 or at == 19:
- s.sprites[sn].blit(pygame.transform.flip(cell, True, False), (x, y))
- elif at == 8 or at == 14 or at == 20:
- s.sprites[sn].blit(pygame.transform.flip(cell, False, True), (x, y))
- elif at == 9 or at == 15 or at == 21:
- s.sprites[sn].blit(pygame.transform.rotate(cell, -90), (x, y))
- elif at == 10 or at == 16 or at == 22:
- s.sprites[sn].blit(pygame.transform.rotate(cell, -180), (x, y))
- elif at == 11 or at == 17 or at == 23:
- s.sprites[sn].blit(pygame.transform.rotate(cell, -270), (x, y))
- # increment/decrement the sprite sheet cell index
- if at > 11 and at < 18:
- ci += 1
- elif at > 17:
- ci -= 1
- scc_index += r
- # this function sets a color to transparent for all of the sprites
- def colorKey(s, color):
- for key, sprite in s.sprites.items():
- sprite.set_colorkey(color)
- # this function creates a rectangle object x1, y1, w, h, x2, y2 to keep track of objects
- class rect:
- def __init__(s, x, y, w, h):
- s.x1, s.y1, s.w, s.h = x, y, w, h
- s.x2, s.y2 = s.x1 + s.w, s.y1 + s.h
- def setX1(s, x):
- s.x1 = x
- s.x2 = x + s.w
- def setY1(s, y):
- s.y1 = y
- s.y2 = y + s.h
- def setXY(s, x, y):
- s.x1, s.y1 = x, y
- s.x2, s.y2 = x + s.w, y + s.h
- def setX2(s, x):
- s.x2 = x
- s.x1 = x - s.w
- def setY2(s, y):
- s.y2 = y
- s.y1 = y - s.h
- def setXY2(s, x, y):
- s.x2, s.y2 = x, y
- s.x1, s.y1 = x - s.w, y - s.h
- def offX1(s, x):
- s.x1 += x
- s.x2 = s.x1 + s.w
- def offY1(s, y):
- s.y1 += y
- s.y2 = s.y1 + s.h
- def pos(s, integer = False, offset = [0, 0]):
- if integer: return [int(s.x1 + offset[0]), int(s.y1 + offset[1])]
- return [s.x1 + offset[0], s.y1 + offset[1]]
- def rect(s, integer = False):
- if integer: return [int(s.x1), int(s.y1), int(s.w), int(s.h)]
- return [s.x1, s.y1, s.w, s.h]
- def box(s, integer = False):
- if integer: return [int(s.x1), int(s.y1), int(s.x2), int(s.y2)]
- return [s.x1, s.y1, s.x2, s.y2]
- def size(s, integer = False):
- if integer: return [int(s.w), int(s.h)]
- return [s.w, s.h]
- def scale(s, value):
- s.x1 *= value
- s.y1 *= value
- s.w *= value
- s.h *= value
- s.x2 = s.x1 + s.w
- s.y2 = s.y1 + s.h
- return s
- def __str__(s):
- return "x1={}, y1={}, w={}, h={} (x2={}, y2={})".format(s.x1, s.y1, s.w, s.h, s.x2, s.y2)
- # how fast the ladybird accelerates on the x-axis
- LADYBIRD_ACCELERATION = 0.1
- # ladybird object
- class ladybird:
- def __init__(s, x, y): # set the starting coordinates x = 0-30, y = 0-23
- global ZX_TILE_SIZE
- # create the rectange to track position and size of ladybird object
- s.rect = rect(x, y, 2, 1).scale(ZX_TILE_SIZE)
- s.vx = 0 # x velocity
- s.vy = 0 # y velocity
- s.platform = None # keep track of a platform if the ladybird lands on it
- s.anim = 'lbjr' # set the name of the sprite to be use in the first instance lbjr = ladybird jump right
- s.animCounter = 0 # cycle through walking frames 0-1
- def draw(s):
- global ZX_SURFACE, SPRITES
- # blit the ladbird sprite to the display surface. remember s.anim is a string containing the name of the sprite
- ZX_SURFACE.blit(SPRITES.sprites[s.anim], s.rect.pos(True))
- def do(s):
- global SPRITES
- global GRAVITY, LADYBIRD_ACCELERATION
- global PLATFORMS
- global ZX_TILE_SIZE
- # check if any keys are pressed
- k = pygame.key.get_pressed()
- moveLegs = True # if left or right is pressed then animate the legs
- if k[pygame.K_RIGHT]:
- s.vx += LADYBIRD_ACCELERATION # positive acceleration moves the ladybird right
- elif k[pygame.K_LEFT]:
- s.vx -= LADYBIRD_ACCELERATION # negative acceleration moves the ladybird left
- else:
- if round(s.vx, 1) == 0: # check if the ladybird has virtually stopped, if yes then don't animate the ladybirds legs
- moveLegs = False
- s.vx /= 1 + LADYBIRD_ACCELERATION # if left or right keys not pressed then deaccelerate the ladybird
- # the ladybird can only jump if on a platform. Here platform will either be None or a platform object
- if k[pygame.K_SPACE] and s.platform:
- s.platform = None # remove reference to the platform object
- s.vy = -4 # set the starting velocity of the jump. Negative velocity moves the lady up
- # animate the legs if applicable
- if moveLegs:
- s.animCounter = 1 - s.animCounter # 0 or 1
- s.rect.offX1(s.vx) # apply acceleration to the ladybirds x-axis: -vx = LEFT, +vx = RIGHT
- # if the ladybird is on a platform. s.platform = None or a platform object
- if s.platform:
- # set the animation string for walking (remember: only if on a platform)
- if s.vx > 0: # if positive x acceleration is applied (ladybird move right) then anim string is lbwr (ladybird walks right)
- s.anim = 'lbwr' + str(s.animCounter + 1) # lbwr1 or lbwr2
- elif s.vx < 0:
- s.anim = 'lbwl' + str(s.animCounter + 1) # lbwl1 or lbwr1
- # check if the ladybird has walked off the platform
- if s.rect.x1 > s.platform.rect.x2 - ZX_TILE_SIZE or s.rect.x2 < s.platform.rect.x1 + ZX_TILE_SIZE:
- s.platform = None # if true then remove reference to platform object
- else:
- # if not on a platform change the ladybird anim to jumping lbjr for positive acceleration, lbjl for negative.
- # jump sprite is used to jumping up and falling
- if s.vx > 0:
- s.anim = 'lbjr'
- elif s.vx < 0:
- s.anim = 'lbjl'
- # apply acceleration to the y-axis. Negative acceleration moves the ladybird up, positive moves it down.
- s.rect.offY1(s.vy)
- s.vy += GRAVITY # apply gravity to y-axis velocity. Note there's no terminal velocity
- # check if the ladybird has collided with a platform
- for p in PLATFORMS.platforms:
- if s.rect.y2 <= p.rect.y1 and s.rect.y2 + s.vy >= p.rect.y1 and not (s.rect.x1 >= p.rect.x2 - ZX_TILE_SIZE or s.rect.x2 <= p.rect.x1 + ZX_TILE_SIZE):
- # if true then y-axis acceleration become zero, the vertical position of the ladybird matches that of the platform
- # and s.platform references the platform object the ladybird has landed on
- s.vy = 0
- s.rect.setY2(p.rect.y1)
- s.platform = p
- # this data helps to assemble the platforms
- PLATFORM_ENDS_LEFT = ['pfel1', 'pfel2'] # there are two types of 'end' for the platforms
- PLATFORM_ENDS_RIGHT = ['pfer1', 'pfer2'] # same as above but reflected
- PLATFORM_MIDDLES = ['pfm1', 'pfm2', 'pfm3'] # there are three types of 'middle' for the platforms
- PLATFORM_PARTS = [PLATFORM_ENDS_LEFT, PLATFORM_MIDDLES, PLATFORM_ENDS_RIGHT] # put the parts into a single list
- PLATFORM_FG_DECORATIONS = ['fgd1', 'fgd2', 'fgg1', 'fgg2', 'fgg3'] # there are five types of foreground decoration eg grass, mushrooms etc
- # this class creates a container for the platform objects
- class platforms:
- # this class creates a platform object
- class platform:
- def __init__(s, x, y, w): # specify where the platform should be placed and it's width
- global PLATFORM_PARTS, PLATFORM_FG_DECORATIONS
- global SPRITES
- global ZX_TILE_SIZE
- # create a rect object to track position and size
- s.rect = rect(x, y, w, 1).scale(ZX_TILE_SIZE)
- # create two surfaces, the foreground which contains the grass and mushrooms etc
- s.foreground = pygame.Surface(s.rect.size(True), pygame.SRCALPHA)
- # and the background which contains the platform itself
- # it's called the background because it's always drawn behind the ladybird
- s.background = s.foreground.copy()
- # build the platform
- # foreground 1st
- for index in range(w):
- s.foreground.blit(SPRITES.sprites[PLATFORM_FG_DECORATIONS[random.randint(0, len(PLATFORM_FG_DECORATIONS) - 1)]], (index * ZX_TILE_SIZE, 0))
- # then the background, or rather the platform itself
- x = 0
- s.background.blit(SPRITES.sprites[PLATFORM_PARTS[0][random.randint(0, len(PLATFORM_PARTS[0]) - 1)]], (x, 0))
- for index in range(w - 2):
- x += ZX_TILE_SIZE
- s.background.blit(SPRITES.sprites[PLATFORM_PARTS[1][random.randint(0, len(PLATFORM_PARTS[1]) - 1)]], (x, 0))
- x += ZX_TILE_SIZE
- s.background.blit(SPRITES.sprites[PLATFORM_PARTS[2][random.randint(0, len(PLATFORM_PARTS[2]) - 1)]], (x, 0))
- # initialise the platform container object
- def __init__(s, count): # specify how many platforms to add to the container at the start
- global ZX_WIDTH, ZX_HEIGHT, ZX_TILE_SIZE
- s.platforms = [] # create empty container list
- s.platforms += [s.platform(0, 23, 32)] # add the platform represents the floor.
- # create [count] number of platforms
- # each platform is not allowed to overlap another platform. Why? Because I say so!
- for index in range(count):
- # create and infinite loop to find space for our platforms
- # if no space is found because the there are too many platforms to fit onto the 'stage'
- # then the game will hang
- while True:
- foundSpace = True # assume space will be found
- # create a set of random coords and width
- zxW = random.randint(3, 8)
- zxX = random.randint(0, ZX_WIDTH - zxW)
- # position the platform on planes multiple of 4 so platforms aren't stacked on top
- # of each other
- zxY = random.randint(3, (ZX_HEIGHT - 4) / 4) * 4
- # create rect object. Not completely necessary but I've got used to working with them
- r = rect(zxX, zxY, zxW, 1).scale(ZX_TILE_SIZE)
- # cycle through the list of platforms in the container. On first run the list contains
- # only the floor which has a rect of [0, 184, 256, 8]
- for p in s.platforms:
- # if the random platform overlaps a platform in the container list then...
- if not (r.x1 >= p.rect.x2 or r.x2 <= p.rect.x1 or r.y1 >= p.rect.y2 or r.y2 <= p.rect.y1):
- # ...we can no longer assume space will be found
- foundSpace = False
- break # break out of the for loop
- # if the randomly selected platform somehow managed to find a space then
- # break the infinite loop
- if foundSpace: break
- # add the platform to the container list and either loop to add another one or...
- s.platforms += [s.platform(zxX, zxY, zxW)]
- # ...exit the initialisation function, all platforms added!
- # draws the platform
- def drawBKGD(s):
- global ZX_SURFACE
- for p in s.platforms:
- ZX_SURFACE.blit(p.background, p.rect.pos(True))
- # draws the platform decorations, grass, mushrooms etc
- def drawFG(s):
- global ZX_SURFACE
- global ZX_TILE_SIZE
- for p in s.platforms:
- ZX_SURFACE.blit(p.foreground, p.rect.pos(True, [0, -ZX_TILE_SIZE]))
- # define needed color(s)
- BLACK = [0, 0, 0]
- BLUE = [116, 164, 163]
- DARK_GRASS = [24, 81, 34]
- # the surface the sprites are blitted to is only 256 x 192 pixels
- # without scale this would be too small to see.
- SCALE = 3
- ZX_TILE_SIZE = 8
- ZX_WIDTH = 32
- ZX_HEIGHT = 24
- # create the dimensions for the primary display surface
- W, H = ZX_WIDTH * ZX_TILE_SIZE, ZX_HEIGHT * ZX_TILE_SIZE
- HW, HH = int(W / 2), int(H / 2)
- FPS = 60
- # initialised pygame
- pygame.init()
- DS = pygame.display.set_mode((W * SCALE, H * SCALE))
- ZX_SURFACE = pygame.Surface((W, H)) # this surface will be scaled to fit the primary display surface
- CLOCK = pygame.time.Clock()
- # create/assemble all the sprites
- SPRITES = spritesSheet(LADYBIRD_SPRITES)
- SPRITES.colorKey(BLACK) # make the sprites background transparent
- GRAVITY = 0.2 # set the strength of gravity
- LADYBIRD = ladybird(16, 0) # creat the ladybird object
- PLATFORMS = platforms(5) # and the platform container object. 5 platforms will be created on initialisation
- # create a surface for the background. Blue with a green strip at the bottom
- BACKGROUND = ZX_SURFACE.copy()
- BACKGROUND.fill(BLUE, [0, 0, W, H - ZX_TILE_SIZE])
- BACKGROUND.fill(DARK_GRASS, [0, H - ZX_TILE_SIZE, W, ZX_TILE_SIZE])
- while True:
- e = pygame.event.get() # read events
- if pygame.key.get_pressed()[pygame.K_ESCAPE]: break # if ESC pressed then exit game
- # copy the blue background to the 256 x 192 surface
- # remember: all the sprites will be drawn to ZX_SURFACE too
- ZX_SURFACE = BACKGROUND.copy()
- # draw the platforms
- PLATFORMS.drawBKGD()
- # if the ladybird is jumping then the decorations are drawn behind the ladybird
- if LADYBIRD.vy < 0: PLATFORMS.drawFG()
- LADYBIRD.draw() # draw the ladybird
- # if ladybird is falling then draw decorations in front of ladybird
- if LADYBIRD.vy >= 0: PLATFORMS.drawFG()
- # move the ladybird
- LADYBIRD.do()
- # blit the sprite surface ZX_SURFACE to the primary display surface scaled up to fit it
- DS.blit(pygame.transform.scale(ZX_SURFACE, (W * SCALE, H * SCALE)), (0, 0))
- # update primary display
- pygame.display.update()
- CLOCK.tick(FPS) # maintain frames per second (60)
- pygame.quit()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement