Last active
December 3, 2017 22:29
-
-
Save Ragzouken/1997903707391e6b2bb36ff14a3cf4ce to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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