diff --git a/gamemenu.py b/gamemenu.py new file mode 100755 index 0000000..f8e363b --- /dev/null +++ b/gamemenu.py @@ -0,0 +1,349 @@ +#!/usr/bin/python3 + +import sys +import time +import enum +import threading +import argparse +import subprocess +import blup.frame +import blup.output + +class Game(object): + def __init__(self): + self.balance = None + self.pygame = False + self.output = None + pass + + @property + def logo(self): + return self._logo + + @property + def args(self): + args = [] + args += ['--players', str(self.players)] + if self.balance is not None: + args += ['--balance', self.balance] + if self.pygame: + args += ['--pygame'] + if self.output is not None: + args += ['--output', self.output] + return args + +class PongGame(Game): + def __init__(self): + self.name = 'Pong' + self.executable = 'balancep0ng.py' + self.players = 0 + self._logo = [ + [1,1,0,0,0,1,1,0,0,1,0,0,1,0,0,1,1,0], + [1,0,1,0,1,0,0,1,0,1,1,0,1,0,1,0,0,0], + [1,1,0,0,1,0,0,1,0,1,0,1,1,0,1,0,1,1], + [1,0,0,0,1,0,0,1,0,1,0,0,1,0,1,0,0,1], + [1,0,0,0,0,1,1,0,0,1,0,0,1,0,0,1,1,0], + ] + +class TetrisGame(Game): + def __init__(self): + self.name = 'Tetris' + self.executable = 'ttrs.py' + self.players = 0 + self._logo = [ + [1,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,0,0,1,1], + [0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,1,0,1,0,0], + [0,1,0,0,1,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0], + [0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,1,0,0,0,1], + [0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,1,0,1,1,0], + ] + +arrow_up = [ +[0,0,1,0,0], +[0,1,1,1,0], +[1,1,1,1,1], +] +arrow_down = list(reversed(arrow_up)) + +arrow_left = [ +[0,0,1], +[0,1,1], +[1,1,1], +[0,1,1], +[0,0,1], +] +arrow_right = [list(reversed(x)) for x in arrow_left] + +go = [ +[0,1,0,0,0,1,0], +[1,0,0,0,1,0,1], +[1,0,1,0,1,0,1], +[0,1,0,0,0,1,0], +] + +select_game = [ +[0,1,1,0,1,1,0,1,0,0,1,1,0,1,1,0,1,1,1], +[1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0], +[0,1,0,0,1,1,0,1,0,0,1,1,0,1,0,0,0,1,0], +[0,0,1,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0], +[1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,1,1,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0], +[0,1,0,0,0,0,1,0,1,0,1,1,0,1,1,0,1,0,0], +[0,1,0,1,1,0,1,1,1,0,1,0,1,0,1,0,1,1,0], +[0,1,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,0], +[0,0,1,1,0,0,1,0,1,0,1,0,0,0,1,0,1,1,0], +] + +player2 = [ +[0,1,1,1,0,0,1,1,1,0,0,0,1,1,0], +[1,0,0,0,1,0,1,0,0,1,0,1,0,0,1], +[0,0,0,1,0,0,1,0,0,1,0,0,0,1,0], +[0,0,1,0,0,0,1,1,1,0,0,0,1,0,0], +[0,1,0,0,0,0,1,0,0,0,0,0,0,0,0], +[1,1,1,1,1,0,1,0,0,0,0,0,1,0,0], +] + +def copy_bitmap(bitmap, frame, color, xoffs=0, yoffs=0): + for x in range(len(bitmap[0])): + for y in range(len(bitmap)): + if bitmap[y][x] == 1: + frame.setPixel(xoffs + x, yoffs + y, color) + +def create_init_screen(dimension): + frame = blup.frame.Frame(dimension) + + xoffs = (dimension.size()[0]-len(select_game[0]))//2 + yoffs = (dimension.size()[1]-len(select_game))//2 + color = (0, 255, 0) + copy_bitmap(select_game, frame, color, xoffs, yoffs) + + return frame + +def create_2player_screen(dimension): + frame = blup.frame.Frame(dimension) + + xoffs = (dimension.size()[0]-len(player2[0]))//2 + yoffs = (dimension.size()[1]-len(player2))//2 + color = (0, 255, 0) + copy_bitmap(player2, frame, color, xoffs, yoffs) + + return frame + +def create_menu_frame(dimension, game): + frame = blup.frame.Frame(dimension) + + xoffs = (dimension.size()[0]-len(arrow_up[0]))//2 + yoffs = 0 + color = (255, 0, 0) + copy_bitmap(arrow_up, frame, color, xoffs, yoffs) + + xoffs = xoffs+len(arrow_up[0])+1 + color = (255, 0, 0) + copy_bitmap(go, frame, color, xoffs, yoffs) + + xoffs = 0 + yoffs = dimension.size()[1]-len(arrow_right) + color = (0, 0, 255) + copy_bitmap(arrow_left, frame, color, xoffs, yoffs) + xoffs = dimension.size()[0]-len(arrow_right[0]) + color = (255, 255, 0) + copy_bitmap(arrow_right, frame, color, xoffs, yoffs) + + xoffs = (dimension.size()[0]-len(game.logo[0]))//2 + yoffs = (dimension.size()[1]-len(game.logo))//2 + color = (0, 255, 255) + copy_bitmap(game.logo, frame, color, xoffs, yoffs) + + return frame + +InputEvent = enum.Enum('InputEvent', ['UP', 'DOWN', 'LEFT', + 'RIGHT', 'QUIT', 'STEP_ON']) + +class AbstractInput(): + def __init__(self): + pass + + def get_event(self): + return None + +class TestInput(AbstractInput): + def __init__(self): + self.__evt = None + + import pygame + self.__pygame = pygame + self.screen = pygame.display.set_mode((100, 100)) + pygame.display.update() + + self.controls = { + pygame.K_a: InputEvent.LEFT, + pygame.K_d: InputEvent.RIGHT, + pygame.K_w: InputEvent.UP, + pygame.K_s: InputEvent.STEP_ON, + pygame.K_ESCAPE: InputEvent.QUIT, + } + + def get_event(self): + pygame = self.__pygame + for event in pygame.event.get(): + if event.type == pygame.KEYDOWN: + return self.controls.get(event.key, None) + return None + +class BalanceInput(AbstractInput, threading.Thread): + def __init__(self, addr, player_id): + self.addr = addr + self.player_id = player_id + self.evt = None + self.oldevt = None + self.player_present = False + threading.Thread.__init__(self, daemon=True) + self.start() + + def run(self): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect(self.addr) + except ConnectionRefusedError: + print('could not connect to balance server', file=sys.stderr) + self.evt = InputEvent.QUIT + return + self.running = True + evt = None + oldevt = None + lastchange = 0 + while self.running: + sock.send(b'a') + data = sock.recv(4) + p0x, p0y, p1x, p1y = struct.unpack('bbbb', data) + if self.player_id == 0: + xbal, ybal = p0x, p0y + elif self.player_id == 1: + xbal, ybal = p1x, p1y + + print('player_id=%d xbal=%d ybal=%d' % (self.player_id, xbal, ybal)) + THRESHOLD = 40 + MIN_TIMES = { + InputEvent.LEFT: 0.1, + InputEvent.RIGHT: 0.1, + InputEvent.UP: 0.1, + InputEvent.DOWN: 0.1, + } + if self.player_present: + if xbal == -128 or ybal == -128: + self.evt = InputEvent.QUIT + self.player_present = False + continue + + if abs(xbal) < THRESHOLD and abs(ybal) < THRESHOLD: + evt = None + else: + if abs(xbal) < abs(ybal): + if ybal > 0: + evt = InputEvent.UP + else: + evt = InputEvent.DOWN + else: + if xbal > 0: + evt = InputEvent.RIGHT + else: + evt = InputEvent.LEFT + if evt != oldevt: + lastchange = time.time() + oldevt = evt + + if time.time() - lastchange < MIN_TIMES.get(evt, 0): + print('player_id=%d debounce %s' % (self.player_id, evt)) + continue + + self.evt = evt + print('player_id=%d event=%s' % (self.player_id, self.evt)) + + else: + if xbal != -128 and ybal != -128: + self.evt = InputEvent.STEP_ON + self.player_present = True + continue + + def get_event(self): + evt = self.evt + self.evt = None + return evt + +def start_game(game, players): + print('Starting', game.name, 'for', players, 'player(s)') + game.players = players + arg = ['./'+game.executable] + game.args + subprocess.call(arg) + sys.exit() + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Blinkenbunt Game Selection Menu') + parser.add_argument('--pygame', dest='pygame', action='store_true', + help='use pygame as input') + parser.add_argument('--balance', dest='balance', type=str, + metavar='HOST:PORT', help='use balance input') + parser.add_argument('--out', dest='out', type=str, metavar='OUTPUT', + default='e3blp', help='blup output specification') + args = parser.parse_args() + + if args.balance is None and not args.pygame: + print('please specify an input method', file=sys.stderr) + sys.exit(1) + + w = 22 + h = 16 + + out = blup.output.getOutput(args.out) + dim = blup.frame.FrameDimension(w, h, 256, 3) + + if args.pygame: + inp1 = TestInput() + inp2 = TestInput() + elif args.balance is not None: + host, port = args.balance.split(':') + inp1 = BalanceInput((host, int(port)), 0) + inp2 = BalanceInput((host, int(port)), 1) + + while inp1.get_event() != InputEvent.STEP_ON: + time.sleep(0.05) + + init_frame = create_init_screen(dim) + out.sendFrame(init_frame) + time.sleep(2) + games = Game.__subclasses__() + game_idx = 0 + while True: + gamecls = games[game_idx] + game = gamecls() + out.sendFrame(create_menu_frame(dim, game)) + + evt = inp1.get_event() + if evt == InputEvent.LEFT: + game_idx -= 1 + if game_idx < 0: + game_idx = len(games)-1 + if evt == InputEvent.RIGHT: + game_idx += 1 + if game_idx >= len(games): + game_idx = 0 + if evt == InputEvent.UP: + game.output = args.out + game.balance = args.balance + game.pygame = args.pygame + break + if evt == InputEvent.QUIT: + sys.exit() + + p2_frame = create_2player_screen(dim) + out.sendFrame(p2_frame) + wait_time = 3 + start = time.time() + while time.time() < start+wait_time: + if inp2.get_event() == InputEvent.STEP_ON: + start_game(game, 2) + time.sleep(0.1) + + start_game(game, 1) +