a Spectrum emulator in Python would probably be too slow, I think. Tried one written in Perl once. It kind of worked, but it wasn't much fun compared to programs like "fuse".
Pygame gave me a hard time in the meantime. I coded some "Space Invaders"-clone in the Spectrum resolution (because the pixel artwork could be found as images somewhere), but I found, somebody else did it all way better than me, so I'm trying to learn from his code at the moment. It may still take some time, but there's some progress.
Here's something, I can already show you (I use Python 2.7 and Pygame 1.9.1, so there may be problems on other configurations). Anyway, here it goes ('q' left, 'w' right, "right shift" jump, 'a' end). Hope you like it . To get the movement accurate, is actually quite tricky. I'd say, I'm about 90% there at the moment:
Code: Select all
#!/usr/bin/python
# coding: utf-8
import pygame
from pygame.locals import *
# License for script code: GNU GPL, v.2.
import os
SCALEFACTOR = 3
class ZXEnvironment:
def __init__(self):
self.zx_paperwidth = 256
self.zx_paperheight = 176
self.zx_char_width = self.zx_paperwidth / 8 # 32
self.zx_char_height = self.zx_paperheight / 8 # 22
self.pc_paperwidth = self.zx_paperwidth * SCALEFACTOR
self.pc_paperheight = self.zx_paperheight * SCALEFACTOR
self.zx_border = 16 # 2
self.pc_border = self.zx_border * SCALEFACTOR
self.screenwidth = (self.zx_paperwidth + 2 * self.zx_border) * SCALEFACTOR
self.screenheight = (self.zx_paperheight + 2 * self.zx_border) * SCALEFACTOR
self.data = ZXData()
self.colours = self.data.colours
def initPaper(self, screen):
self.screen = screen
self.paper = pygame.Surface((self.pc_paperwidth, self.pc_paperheight))
self.paperpos = (self.pc_border, self.pc_border)
self.cls()
def cls(self, do_blit = 1, colourname = "white"):
self.paper.fill(self.colours[colourname])
self.papercolourname = colourname
if do_blit:
self.screen.blit(self.paper, self.paperpos)
def border(self, colourname):
bars = (pygame.Surface((self.screenwidth, self.pc_border)),
pygame.Surface((self.pc_border, self.pc_paperheight)))
pos = { 0 : ((0, 0), (0, self.pc_border + self.pc_paperheight)),
1 : ((0, self.pc_border),
(self.pc_border + self.pc_paperwidth, self.pc_border))}
for i in range(len(bars)):
bars[i].fill(self.colours[colourname])
for u in pos[i]:
self.screen.blit(bars[i], u)
self.bordercolourname = colourname
def zxToPCxy(self, zx_x, zx_y):
x = zx_x * SCALEFACTOR
y = zx_y * SCALEFACTOR
return (x, y)
class Main:
def __init__(self):
os.environ['SDL_VIDEO_WINDOW_POS'] = "218, 5"
self.zxenv = ZXEnvironment()
pygame.init()
self.screen = pygame.display.set_mode((self.zxenv.screenwidth, self.zxenv.screenheight))
pygame.display.set_caption('Hello Willy!')
self.keys = None
self.zxenv.initPaper(self.screen)
self.zxenv.cls(1, "black")
self.bordercolour = "red"
self.zxenv.border(self.bordercolour)
self.initSprites()
self.run()
def run(self):
while True:
self.clock = pygame.time.Clock()
self.clock.tick(10)
self.zxenv.cls(do_blit = 0, colourname = self.zxenv.papercolourname)
r = self.processEvents()
if r == "quit":
return r
self.mw.update(self.zxenv.paper, self.keys)
self.screen.blit(self.zxenv.paper, self.zxenv.paperpos)
pygame.display.flip()
def processEvents(self):
self.keys = []
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return "quit"
if event.type == pygame.KEYDOWN:
self.keys.append(event.key)
self.keys = pygame.key.get_pressed()
if self.keys[K_a]:
pygame.quit()
return "quit"
return 0
def initSprites(self):
self.mw = Willy("Willy", 30, 80, self.zxenv, "white")
class UDGSprite(pygame.sprite.Sprite):
def __init__(self, name, zx_x, zx_y, zxenv, inkcolourname):
pygame.sprite.Sprite.__init__(self)
self.name = name
self.zxenv = zxenv
self.zx_position = (zx_x, zx_y)
self.pc_position = self.zxenv.zxToPCxy(zx_x, zx_y)
self.colours = self.zxenv.colours
self.inkcolourname = inkcolourname
self.data = SpriteData()
self.imagedata = self.data.getImagedata(self.name)
self.images = []
self.zx_imagesizes = []
self.pc_imagesizes = []
self.createImages()
self.currentimage = 0
self.image = None
self.rect = None
self.active = False
self.xmoved = 0
self.ymoved = 0
def setImage(self, imagenumber):
self.currentimage = imagenumber
self.image = self.images[imagenumber]
self.rect = pygame.Rect(self.pc_position,
self.pc_imagesizes[imagenumber])
def zx_moveTo(self, zx_x, zx_y):
self.zx_position = (zx_x, zx_y)
self.pc_position = self.zxenv.zxToPCxy(zx_x, zx_y)
self.rect = pygame.Rect(self.pc_position,
self.pc_imagesizes[self.currentimage])
def zx_moveBy(self, move_zx_x, move_zx_y):
self.zx_position = (self.zx_position[0] + move_zx_x,
self.zx_position[1] + move_zx_y)
self.pc_position = (self.pc_position[0] + move_zx_x * SCALEFACTOR,
self.pc_position[1] + move_zx_y * SCALEFACTOR)
self.rect = pygame.Rect(self.pc_position,
self.pc_imagesizes[self.currentimage])
def createImages(self):
a = self.imagedata.keys()
a.sort()
for i in a:
b = i.split("_")
if len(b) == 1:
num = 0
else:
num = int(b[1])
b = []
numstr = "{0:0" + str(self.imagedata[i][0]) + "b}"
for u in self.imagedata[i][1]:
binstring = numstr.format(u)
b.append(binstring)
zx_spritesize = (len(b[0]), len(b))
pc_spritesize = (zx_spritesize[0] * SCALEFACTOR,
zx_spritesize[1] * SCALEFACTOR)
surface = pygame.Surface(pc_spritesize)
surface = pygame.Surface.convert_alpha(surface)
surface = self.plotImage(surface, b, zx_spritesize[0], self.inkcolourname)
self.images.append(surface)
self.zx_imagesizes.append(zx_spritesize)
self.pc_imagesizes.append(pc_spritesize)
def plotImage(self, surface, data_, spritewidth, inkcolourname):
pxarray = pygame.PixelArray(surface)
t_x = 0
t_y = 0
for line in data_:
for bit in line:
if bit == "1":
# Plot one ZX pixel:
for pixelline in range(SCALEFACTOR):
for pixelrow in range(SCALEFACTOR):
pxarray[t_x * SCALEFACTOR + pixelline][t_y * SCALEFACTOR + pixelrow] = self.colours[inkcolourname]
else:
# Plot one ZX pixel:
for pixelline in range(SCALEFACTOR):
for pixelrow in range(SCALEFACTOR):
pxarray[t_x * SCALEFACTOR + pixelline][t_y * SCALEFACTOR + pixelrow] = (0, 0, 0, 0)
t_x += 1
# Next line (like a typewriter):
t_y += 1
t_x -= spritewidth
del pxarray
return surface
class Willy(UDGSprite):
def __init__(self, name, zx_x, zx_y, zxenv, inkcolourname):
UDGSprite.__init__(self, name, zx_x, zx_y, zxenv, inkcolourname)
# Willy's movement is actually quite strange:
self.imagepos = (0, 2, 0, 1, 3, 4, 3, 5)
self.moves = (3, 3, 1, 1, 3, 3, 1, 1)
self.ind = 0
self.lifted = (-3, -4, -8, -4, -3, 0, 0, 0, 0, 3, 4, 8, 4, 3)
self.jumping = False
self.lindex = 0
self.direction = 0
def update(self, surface, keys, *args):
self.setImage(self.imagepos[self.ind])
if self.jumping:
self.jump()
surface.blit(self.image, self.pc_position)
return
if keys[K_q]:
self.direction = -1
self.go_left()
if keys[K_w]:
self.direction = 1
self.go_right()
if not keys[K_q] and not keys[K_w]:
self.direction = 0
if keys[K_RSHIFT]:
self.jumping = True
surface.blit(self.image, self.pc_position)
def go_left(self):
if self.ind < 4:
self.ind += 4
if self.zx_position[0] > 0:
self.zx_moveBy(- self.moves[self.ind], 0)
self.ind += 1
if self.ind > 7:
self.ind = 4
def go_right(self):
if self.ind > 3:
self.ind -= 4
if self.zx_position[0] < self.zxenv.zx_paperwidth - self.zx_imagesizes[self.currentimage][0]:
self.zx_moveBy(self.moves[self.ind], 0)
self.ind += 1
if self.ind > 3:
self.ind = 0
def jump(self):
if self.direction == -1:
self.go_left()
elif self.direction == 1:
self.go_right()
self.zx_moveBy(0, self.lifted[self.lindex])
self.lindex += 1
if self.lindex > len(self.lifted) - 1:
self.lindex = 0
self.jumping = 0
class ZXData:
def __init__(self):
self.colours = {"black" : (0, 0, 0),
"blue" : (0, 0, 197),
"red" : (189, 0, 0),
"magenta" : (189, 0, 197),
"green" : (0, 190, 0),
"cyan" : (0, 190, 197),
"yellow" : (189, 190, 0),
"white" : (189, 190, 197)}
class SpriteData:
def __init__(self):
self.imagedata = {'Willy_0' : (8, (6, 62, 124, 52, 62, 60, 24, 60, 126, 126, 247, 251, 60, 118, 110, 119)),
'Willy_1' : (10, (12, 124, 248, 104, 124, 120, 48, 120, 252, 510, 1023, 891, 124, 237, 391, 454)),
'Willy_2' : (6, (3, 31, 62, 26, 31, 30, 12, 30, 55, 55, 55, 59, 30, 12, 12, 14)),
'Willy_3' : (8, (96, 124, 62, 44, 124, 60, 24, 60, 126, 126, 239, 223, 60, 110, 118, 238)),
'Willy_4' : (10, (192, 248, 124, 88, 248, 120, 48, 120, 252, 510, 1023, 891, 248, 732, 902, 398)),
'Willy_5' : (6, (48, 62, 31, 22, 62, 30, 12, 30, 59, 59, 59, 55, 30, 12, 12, 28))}
def getImagedata(self, spritename):
imagedataslice = {}
for i in self.imagedata.keys():
if i.startswith(spritename):
imagedataslice[i] = self.imagedata[i]
return imagedataslice
if __name__ == '__main__':
Main()
I'm not sure, if I have the will to go through all of that. Back then, they could at least put on a tape, what they had created, and sell it.