Skip to content

Instantly share code, notes, and snippets.

@Ragzouken
Last active December 3, 2017 22:29
Show Gist options
  • Save Ragzouken/1997903707391e6b2bb36ff14a3cf4ce to your computer and use it in GitHub Desktop.
Save Ragzouken/1997903707391e6b2bb36ff14a3cf4ce to your computer and use it in GitHub Desktop.
import pygame
from time import sleep
import random
import urllib2
# CONFIG #
SCREEN = (480, 272)
ALIGN = "LEFT" # "CENTER" "RIGHT"
ROTATE = 1 # 1 2 3
TEXT_DELAY = 50 #ms
##########
pygame.init()
pygame.mouse.set_visible(False)
white = (255,255,255)
black = (0,0,0)
red = (255,0,0)
green = (0,155,0)
display_width = 480
display_height = 272
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('bitspy')
clock = pygame.time.Clock()
block_size = 10
FPS = 15
font = pygame.font.SysFont(None, 25)
renders = {}
class WorldParser:
def __init__(self, lines):
self.lines = lines
self.index = 0
self.world = {
"palettes": {},
"rooms": {},
"tiles": {},
"sprites": {},
"dialogues": {},
"endings": {},
}
def add_object(self, type, object):
self.world[type][object["id"]] = object
def parse(self):
self.world["title"] = self.take_line()
while self.index < len(self.lines):
if self.check_line("PAL "):
self.parse_palette()
elif self.check_line("ROOM "):
self.parse_room()
elif self.check_line("TIL "):
self.parse_tile()
elif self.check_line("SPR "):
self.parse_sprite()
elif self.check_line("DLG "):
self.parse_dialogue()
elif self.check_line("END "):
self.parse_ending()
else:
self.skip_line()
def take_line(self):
line = self.lines[self.index]
self.index += 1
return line
def skip_line(self):
line = self.take_line()
if line.strip():
print("skipping: " + line)
def peek_line(self):
return self.lines[self.index]
def check_line(self, start):
return self.peek_line().startswith(start)
def take_split(self, delimiter):
return self.take_line().split(delimiter)
def parse_palette(self):
palette = {}
_, palette["id"] = self.take_split(" ")
palette["name"] = self.parse_name()
palette["colors"] = [self.parse_color(), self.parse_color(), self.parse_color()]
self.add_object("palettes", palette)
def parse_color(self):
return [int(c) for c in self.take_split(",")]
def parse_room(self):
room = {
"exits": [],
"endings": [],
}
_, room["id"] = self.take_split(" ")
room["tilemap"] = [self.take_split(",") for y in xrange(0, 16)]
room["name"] = self.parse_name()
while not self.check_line("PAL"):
if self.check_line("EXT "):
room["exits"].append(self.parse_exit())
elif self.check_line("WAL "):
room["walls"] = self.parse_room_walls()
elif self.check_line("END "):
room["endings"].append(self.parse_room_ending())
else:
print("skipping " + self.peek_line())
self.take_line()
_, room["palette"] = self.take_split(" ")
self.add_object("rooms", room)
def parse_exit(self):
exit = {
"dest": {},
}
_, pos1, room, pos2 = self.take_split(" ")
exit["x"], exit["y"] = (int(c) for c in pos1.split(","))
exit["dest"]["room"] = room
exit["dest"]["x"], exit["dest"]["y"] = (int(c) for c in pos2.split(","))
return exit
def parse_room_ending(self):
ending = {}
_, ending["id"], pos = self.take_split(" ")
ending["x"], ending["y"] = (int(c) for c in pos.split(","))
return ending
def parse_room_walls(self):
_, row = self.take_split(" ")
return row.split(",")
def parse_ending(self):
ending = {}
_, ending["id"] = self.take_split(" ")
ending["text"] = self.take_line()
self.add_object("endings", ending)
def parse_tile(self):
tile = {}
_, tile["id"] = self.take_split(" ")
tile["graphic"] = self.parse_graphic()
tile["name"] = self.parse_name()
self.add_object("tiles", tile)
def parse_sprite(self):
sprite = {
"room": None,
"x": 0,
"y": 0,
"dialogue": "",
}
_, sprite["id"] = self.take_split(" ")
sprite["graphic"] = self.parse_graphic()
if self.check_line("DLG "):
_, sprite["dialogue"] = self.take_line().split(" ", 1)
if self.check_line("POS "):
_, sprite["room"], pos = self.take_split(" ")
sprite["x"], sprite["y"] = (int(c) for c in pos.split(","))
self.add_object("sprites", sprite)
def parse_dialogue(self):
dialogue = {
"text": "",
}
_, dialogue["id"] = self.take_split(" ")
if self.check_line('"""'):
self.take_line()
while not self.check_line('"""'):
dialogue["text"].append(self.take_line())
dialogue["text"] = "\n".join(dialogue["text"])
else:
dialogue["text"] = self.take_line()
self.add_object("dialogues", dialogue)
def parse_graphic(self):
graphic = [self.parse_frame()]
if self.check_line(">"):
self.take_line()
graphic.append(self.parse_frame())
return graphic
def parse_frame(self):
return [[b == "1" for b in self.take_line()] for y in xrange(0, 8)]
def parse_name(self):
if self.check_line("NAME "):
return self.take_split(" ")[1]
else:
return "unnamed"
world = {}
palette = {}
def load_game():
off = 8
gameDisplay.fill(white)
pygame.draw.rect(gameDisplay,
black,
[off, off, display_width - off * 2, display_height - off * 2])
pygame.display.update()
game = {}
data = ""
with open("game.bitsy.txt", "rb") as file:
data = file.read().replace("\r\n", "\n")
url = "https://gist.githubusercontent.com/Ragzouken/7302551592a3729dc2fbfdbfc5201735/raw/3c9a95d50f1a2d75c0c39d131a6d3eeff1ce69dc/pier"
url = "https://gist.githubusercontent.com/Ragzouken/af6a2bf197ed34426a030d73521626c3/raw/56f376130b5fa2286a81ee043c38cd5a849f49d3/castle"
lines = data.split("\n") #urllib2.urlopen(url).read().decode('utf-8').split("\n")
i = 0
parser = WorldParser(lines)
parser.parse()
global world
world = parser.world
screen = pygame.Surface((256, 256))
avatar = pygame.Surface((16, 16))
textbox = pygame.Surface((208, 38))
room_frame_0 = pygame.Surface((256, 256))
room_frame_1 = pygame.Surface((256, 256))
font = [pygame.Surface((6, 8)) for i in xrange(256)]
arrow = pygame.Surface((5, 3))
avatar_x = 0
avatar_y = 0
avatar_room = None
dialogue = []
dialogue_char = 0
starting = True
ending = False
def draw_graphic(surface, ox, oy, tile, anim, primary):
graphic = tile["graphic"]
frame = graphic[anim % len(graphic)]
for y in xrange(0, 8):
for x in xrange(0, 8):
color = primary if frame[y][x] else 0
pygame.draw.rect(surface, color, [x * 2 + ox * 16, y * 2 + oy * 16, 2, 2])
def render(graphic, primary):
renders = [pygame.Surface((16, 16)), pygame.Surface((16, 16))]
for i in xrange(2):
for y in xrange(0, 8):
for x in xrange(0, 8):
color = primary if graphic[i % len(graphic)][y][x] else BGR
pygame.draw.rect(renders[i], color, [x * 2, y * 2, 2, 2])
return renders
BLK = 0x000000
WHT = 0xFFFFFF
BGR = 0x999999
TIL = 0xFF0000
SPR = 0xFFFFFF
palette = None
def render_font():
global arrow
data = ""
with open("font.txt", "rb") as file:
data = file.read().replace("\r\n", "\n")
sections = data.split("\n\n")
for i, section in enumerate(sections):
pixels = pygame.PixelArray(font[i])
for y, row in enumerate(section.split("\n")):
for x, char in enumerate(row):
pixels[x, y] = BLK if char == "0" else WHT
arrowdata = """11111\n01110\n00100"""
pixels = pygame.PixelArray(arrow)
for y, row in enumerate(arrowdata.split("\n")):
for x, char in enumerate(row):
pixels[x, y] = BLK if char == "0" else WHT
arrow = pygame.transform.scale(arrow, (10, 6))
def recolor(surface, palette):
pixels = pygame.PixelArray(surface)
pixels.replace(BGR, palette[0])
pixels.replace(TIL, palette[1])
pixels.replace(SPR, palette[2])
def rotate(surface, count):
pixels = pygame.PixelArray(surface)
def draw_room(surface, room, frame):
for y in xrange(0, 16):
for x in xrange(0, 16):
id = room["tilemap"][y][x]
if id == "0":
pygame.draw.rect(surface, BGR, [x * 16, y * 16, 16, 16])
continue
tile = world["tiles"][id]
surface.blit(renders["tile_" + id][frame], (x * 16, y * 16))
#draw_graphic(surface, x, y, tile, frame, 1)
for sprite in world["sprites"].values():
if sprite["id"] != "A" and sprite["room"] == room["id"]:
surface.blit(renders["sprite_" + sprite["id"]][frame], (sprite["x"] * 16, sprite["y"] * 16))
#for exit in room["exits"]:
# pygame.draw.rect(surface, SPR, [exit["x"] * 16 + 2, exit["y"] * 16 + 2, 12, 12])
recolor(surface, palette)
def draw(frame):
global screen
gameDisplay.fill(black)
gap_h = SCREEN[0] - 256
gap_v = SCREEN[1] - 256
pad_x = gap_h // 2
pad_y = gap_v // 2
if ALIGN == "LEFT":
pad_x = pad_y
elif ALIGN == "CENTER":
pad_x = gap_h // 2
elif ALIGN == "RIGHT":
pad_x = SCREEN[0] - 256 - pad_y
d_x = 24
d_y = 24
if not ending and not starting:
room = room_frame_0 if frame == 0 else room_frame_1
screen.blit(room, (0, 0))
avi = renders["sprite_A"][frame]
avatar.blit(avi, (0, 0))
recolor(avatar, palette)
if avatar_y < 8:
d_y = 194
screen.blit(avatar, (avatar_x * 16, avatar_y * 16))
else:
screen.fill(palette[0])
d_y = 108
if dialogue:
screen.blit(textbox, (d_x, d_y))
screen2 = pygame.transform.rotate(screen, -90 * ROTATE)
#screen2 = pygame.transform.scale(screen, (512, 512))
#screen2 = pygame.transform.smoothscale(screen, ((272, 272)))
#pad_y = 0
gameDisplay.blit(screen2, (pad_x, pad_y))
pygame.display.update()
def set_room(room):
global palette
palette = world["palettes"][room["palette"]]["colors"]
draw_room(room_frame_0, room, 0)
draw_room(room_frame_1, room, 1)
def pre_render():
i = 0
for sprite in world["sprites"].values():
renders["sprite_" + sprite["id"]] = render(sprite["graphic"], SPR)
for tile in world["tiles"].values():
renders["tile_" + tile["id"]] = render(tile["graphic"], TIL)
#items
def use_exit(exit):
global avatar_x, avatar_y, avatar_room
dest = exit["dest"]
avatar_room = dest["room"]
avatar_x = dest["x"]
avatar_y = dest["y"]
set_room(world["rooms"][avatar_room])
def use_ending(ending_):
global ending
generate_dialogue(world["endings"][ending_["id"]]["text"])
ending = True
def move_into(x, y):
global avatar_x
global avatar_y
room = world["rooms"][avatar_room]
tile = room["tilemap"][y][x]
for sprite in world["sprites"].values():
if sprite["room"] == avatar_room and sprite["x"] == x and sprite["y"] == y and sprite["id"] != "A":
generate_dialogue(world["dialogues"][sprite["dialogue"]]["text"])
return
if not tile in room["walls"]:
avatar_x = x
avatar_y = y
for ending in room["endings"]:
if ending["x"] == avatar_x and ending["y"] == avatar_y:
use_ending(ending)
return
for exit in room["exits"]:
if exit["x"] == avatar_x and exit["y"] == avatar_y:
use_exit(exit)
return
def generate_dialogue(text):
lines = text.split("\n")
rows = []
limit = 32
for line in lines:
if len(line) > limit:
row = ""
words = line.split(" ")
next = 0
while next < len(words):
if len(words[next]) > limit:
space = limit - 1 - len(row)
rows.append(line[:left])
words[next] = line[left:]
next -= 1
elif len(row) + 1 + len(words[next]) > limit:
rows.append(row)
row = words[next]
elif row:
row += " " + words[next]
else:
row = words[next]
next += 1
if row:
rows.append(row)
else:
rows.append(line)
dialogue.extend(rows)
def draw_next_char():
global dialogue_char
xoff = 8
yoff = 8
chars = 32
demo = "".join(line.ljust(chars) for line in dialogue[:2])
demo = demo[:chars*2]
if dialogue_char >= len(demo):
return False
x = dialogue_char % 32
y = dialogue_char // 32
c = demo[dialogue_char]
textbox.blit(font[ord(c)], (xoff + x * 6, yoff + y * (8 + 4)))
dialogue_char += 1
while dialogue_char < len(demo) and demo[dialogue_char].strip() == "":
dialogue_char += 1
if dialogue_char == len(demo):
textbox.blit(arrow, (xoff + 182, yoff + 20))
return True
def skip_dialogue():
global dialogue_char
skipped = False
while draw_next_char():
skipped = True
return skipped
exit = False
def advance_dialogue():
global dialogue, dialogue_char, exit, starting
if skip_dialogue():
return
if ending:
exit = True
if starting:
starting = False
dialogue = dialogue[2:]
textbox.fill(BLK)
draw_next_char()
dialogue_char = 0
def game_loop():
global ROTATE, ALIGN
global avatar_x, avatar_y, avatar_room, dialogue, exit
anim = 0
pre_render()
avatar_x = world["sprites"]["A"]["x"]
avatar_y = world["sprites"]["A"]["y"]
avatar_room = world["sprites"]["A"]["room"]
set_room(world["rooms"][avatar_room])
dir = -1
key = False
generate_dialogue(world["title"])
while not exit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit = True
if event.type == pygame.KEYDOWN:
key = True
if event.key == pygame.K_LEFT:
dir = 2
elif event.key == pygame.K_RIGHT:
dir = 0
elif event.key == pygame.K_UP:
dir = 3
elif event.key == pygame.K_DOWN:
dir = 1
elif event.key == pygame.K_q:
exit = True
elif event.key == pygame.K_ESCAPE:
exit = True
elif event.key == pygame.K_0:
ROTATE = 0
elif event.key == pygame.K_1:
ROTATE = 1
elif event.key == pygame.K_2:
ROTATE = 2
elif event.key == pygame.K_3:
ROTATE = 3
elif event.key == pygame.K_i:
ALIGN = "LEFT"
elif event.key == pygame.K_o:
ALIGN = "CENTER"
elif event.key == pygame.K_p:
ALIGN = "RIGHT"
if (anim % 3 == 0):
if dialogue:
if key:
advance_dialogue()
else:
if dir >= 0:
dir = (dir - ROTATE) % 4
if dir == 2:
move_into(max(0, avatar_x - 1), avatar_y)
elif dir == 0:
move_into(min(15, avatar_x + 1), avatar_y)
elif dir == 3:
move_into(avatar_x, max(0, avatar_y - 1))
elif dir == 1:
move_into(avatar_x, min(15, avatar_y + 1))
dir = -1
key = False
draw((anim // 6) % 2)
if dialogue:
draw_next_char()
draw((anim // 6) % 2)
clock.tick(FPS)
anim += 1
pygame.mouse.set_visible(True)
pygame.quit()
quit()
if __name__ == "__main__":
render_font()
load_game()
game_loop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment