def build_gui(self): """ Add widgets to the screen. Widgets on the left need only be reblitted when they get dirty. Widgets that overlay the world screen need to be reblitted every frame. """ # start adding widgets leftgui = LayeredDirty() # only reblit when dirty=1 overlaygui = LayeredUpdates() # reblit every frame w, h = self.win_size line_h = config_get_fontsize() gui_w = self.gui_offset # -- name label at top-left of the screen rec = pygame.Rect(0, 0, gui_w - 1, line_h - 1) evt_txt_dict = {MMyNameChangedEvent: 'newname', MGreetNameEvt:'newname'} txtcol = config_get_txtlabel_txtcolor() bgcol = config_get_txtlabel_bgcolor() namebox = TextLabelWidget(self._em, '', events_attrs=evt_txt_dict, rect=rec, txtcolor=txtcol, bgcolor=bgcol) leftgui.add(namebox) # -- list of connected players, until middle of the screen rec = pygame.Rect(0, line_h, gui_w - 1, line_h - 1) txt = 'Connected players:' txtcol = config_get_txtlabel_txtcolor() bgcol = config_get_txtlabel_bgcolor() whosonlinetitle = TextLabelWidget(self._em, txt, rect=rec, txtcolor=txtcol, bgcolor=bgcol) leftgui.add(whosonlinetitle) rec = pygame.Rect(0, 2 * line_h, gui_w - 1, h / 2 - 2 * line_h - 1) numlines = int(rec.height / line_h) txtcol = config_get_chatlog_txtcolor() bgcol = None #config_get_chatlog_bgcolor() whosonlinebox = PlayerListWidget(self._em, numlines, rect=rec, txtcolor=txtcol, bgcolor=bgcol) leftgui.add(whosonlinebox) # -- chat window overlay at bottom of the world screen chat_height = h / 4 numlines = int(chat_height / line_h) if numlines > 0: # text input field rec = pygame.Rect(gui_w + 1, h - line_h, w - gui_w - 1, line_h - 1) chatbox = InputFieldWidget(self._em, rect=rec) overlaygui.add(chatbox) if numlines > 1: # text display line rec = pygame.Rect(gui_w + 1, h * 3 / 4, w - gui_w - 1, h / 4 - line_h - 1) txtcol = config_get_chatlog_txtcolor() # no bg color to disply on top of world screen chatwindow = ChatLogWidget(self._em, numlines=numlines, rect=rec, txtcolor=txtcol) overlaygui.add(chatwindow) self.left_gui_sprites = leftgui self.overlay_gui_sprites = overlaygui
class Scene(ABC): REGULAR_FONT = "fonts/Connection/Connection.otf" BOLD_FONT = "fonts/Connection/ConnectionBold.otf" def __init__(self, controller): try: open(self.REGULAR_FONT).close() except FileNotFoundError: fix_path() self._font = ftfont.Font(self.REGULAR_FONT, 50) self._small_font = ftfont.Font(self.REGULAR_FONT, 30) self._bold_font = ftfont.Font(self.BOLD_FONT, 30) self.__controller = controller try: self.__last_scene = controller.get_scene() except AttributeError: self.__last_scene = None self.game_objects = LayeredUpdates() self.screen_rect = pygame.display.get_surface().get_rect() background = pygame.Surface(self.screen_rect.size) background.fill((240, 240, 240)) self.background = GameObject(self.game_objects, pygame.Rect(0, 0, 0, 0), background) self.game_objects.add(self.background) @abstractmethod def handle_events(self, events): pass @abstractmethod def update(self, ms): game_objects = self.game_objects game_objects.update(ms) collisions = groupcollide(game_objects, game_objects, False, False) for key, value in collisions.items(): value.pop(0) if value: try: key.collide(value) except AttributeError: pass @abstractmethod def render(self, screen): return self.game_objects.draw(screen) def load(self): pass def back(self): # return to last scene self.__controller.set_scene(self.__last_scene) def set_scene(self, SceneClass: 'Scene', *args): self.__controller.set_scene(SceneClass(self.__controller, *args))
def render_bot_icons(board_render): ICON_SIZE = (50, 50) group = LayeredUpdates() SEAT_POS = [ None, (board_render.surface.get_width() - ICON_SIZE[0] - 10, 80), (460 - ICON_SIZE[0] - 10, 10), (10, 80) ] game_view = board_render.match.game_manager.get_active_view() icon_cache = game_view.bot_icon_cache ais = game_view.ai_list ai_icons = [] dud_ai_icon = pygame.Surface(ICON_SIZE, pygame.SRCALPHA) load_image_resource("dudB.png", dud_ai_icon, size=ICON_SIZE) for i in range(4): for icon in icon_cache: if icon['ai'] == ais[i]: surface = pygame.Surface(ICON_SIZE, pygame.SRCALPHA) load_image_resource(icon['icon'], surface, size=ICON_SIZE) ai_icons += [surface] break is_story_mode = hasattr(game_view, "match_dict") for seat in range(4): if seat == 0: continue sprite = Sprite() sprite.rect = (SEAT_POS[seat], ICON_SIZE) sprite.layer = 0 if is_story_mode and seat != 2: sprite.image = dud_ai_icon else: sprite.image = ai_icons[seat] group.add(sprite) return group
def render_center_info(board_render): center_info_surface = Surface((250, 250)) match = board_render.match group = LayeredUpdates() # Background info_rect = Rect(0, 0, 250, 250) info_rect.center = board_render.surface.get_rect().center pygame.draw.rect(center_info_surface, (0, 0, 0), info_rect) current_path = os.path.dirname(os.path.realpath(__file__)) font_path = os.path.join( current_path, "..", "..", "resources", "fonts", "SourceSans3-Semibold.ttf" ) font = Font(font_path, 30) large_font = Font(font_path, 42) # Round Title round_text = "{} {}".format("East" if match.east_prevalent else "South", match.round_number) round_surface = font.render(round_text, True, (255, 255, 255)) round_sprite = Sprite() round_sprite.image = round_surface round_sprite.rect = round_surface.get_rect() round_sprite.rect.center = info_rect.center round_sprite.rect.y -= (round_surface.get_rect().height // 2) round_sprite.layer = 1 group.add(round_sprite) # Tiles Left in Wall tile_count_surface = large_font.render(str(len(match.current_board.wall)), True, (255, 255, 255)) wall_remaining_sprite = Sprite() wall_remaining_sprite.image = tile_count_surface wall_remaining_sprite.rect = tile_count_surface.get_rect() wall_remaining_sprite.rect.center = info_rect.center wall_remaining_sprite.rect.y += (tile_count_surface.get_rect().height // 3) wall_remaining_sprite.layer = 1 group.add(wall_remaining_sprite) # Wind Markers create_wind_markers(match, info_rect, group) # Turn Marker create_turn_marker(match, info_rect, group) # Riichi Markers create_riichi_markers(match, info_rect, group) background_sprite = Sprite() background_sprite.rect = info_rect background_sprite.image = center_info_surface background_sprite.layer = 0 group.add(background_sprite) return group
def render_score_screen(board_render): should_show_screen = board_render.board_manager.round_should_end group = LayeredUpdates() if not should_show_screen: return group current_path = os.path.dirname(os.path.realpath(__file__)) font_path = os.path.join( current_path, "..", "..", "resources", "fonts", "SourceSans3-Semibold.ttf" ) font = Font(font_path, 40) font_small = Font(font_path, 24) match = board_render.match scores = match.scores delta_scores = match.delta_scores total_scores = list(map(lambda s: s[0] + s[1], zip(scores, delta_scores))) winner_indices = numpy.argwhere(numpy.array(delta_scores) > 0).flatten() winner_names = [] array = numpy.array(total_scores) temp = array.argsort()[::-1] ranks = numpy.empty_like(temp) ranks[temp] = numpy.arange(len(array)) icons = get_object('boticons')['bot'] screen_surface = Surface(board_render.surface.get_size(), pygame.SRCALPHA) screen_surface.fill((0, 0, 0)) ROUND_COMPLETE = "Round Complete" round_complete_surface = font.render(ROUND_COMPLETE, True, (255, 255, 255)) font_width, font_height = font.size(ROUND_COMPLETE) icon_size = (100, 100) player_list = match.ai_list x = screen_surface.get_width() // 5 y = font_height + 5 for seat in range(4): ai_name = player_list[seat] name = None icon = None for entry in icons: if entry['ai'] == ai_name: name = entry['name'] icon = entry['icon'] break if name is None: raise "BOT WAS NOT DEFINED." if seat in winner_indices: winner_names += [name] icon_surface = Surface(icon_size, pygame.SRCALPHA) load_image_resource(icon, icon_surface, size=icon_size) screen_surface.blit( icon_surface, (x, y) ) player_name = font.render(name, True, (255, 255, 255)) _, name_h = font.size(name) screen_surface.blit( player_name, (x + icon_size[0] + 10, y) ) score_string = "{} Points".format(scores[seat]) score_render = font_small.render(score_string, True, (255, 255, 255)) _, score_h = font_small.size(score_string) screen_surface.blit( score_render, (x + icon_size[0] + 10, y + name_h + 5) ) delta_string = "{}{}".format("+" if delta_scores[seat] >= 0 else "", delta_scores[seat]) delta_render = font_small.render(delta_string, True, (255, 255, 255)) _, delta_h = font_small.size(delta_string) screen_surface.blit( delta_render, (x + icon_size[0] + 10, y + name_h + 5 + score_h + 5) ) total_string = "Total: {} Points".format(total_scores[seat]) total_render = font_small.render(total_string, True, (255, 255, 255)) screen_surface.blit( total_render, (x + icon_size[0] + 10, y + name_h + 5 + score_h + 5 + delta_h + 5) ) place_string = "{}".format(ranks[seat] + 1) place_render = font.render(place_string, True, (255, 255, 255)) place_w, place_h = font.size(place_string) screen_surface.blit( place_render, (x - place_w - 5, y + ((icon_size[1] - place_h) // 2)) ) y += icon_size[1] + 70 LOADING_NEXT_ROUND = "Loading next Round..." loading_surface = font.render(LOADING_NEXT_ROUND, True, (255, 255, 255)) loading_width, loading_height = font.size(LOADING_NEXT_ROUND) screen_surface.blit( loading_surface, (screen_surface.get_width() - loading_width - 10, screen_surface.get_height() - loading_height - 10) ) screen_surface.blit( round_complete_surface, ((screen_surface.get_width() // 2) - (font_width // 2), 10), ) result_pos = (screen_surface.get_width() * 0.6, screen_surface.get_height() // 3) if board_render.board_manager.did_exhaustive_draw: EXHAUSTIVE = "Exhaustive Draw" exhaustive_surface = font.render(EXHAUSTIVE, True, (255, 255, 255)) screen_surface.blit( exhaustive_surface, result_pos ) else: WINNERS = "Winners:" winners = ", ".join(winner_names) winner_text = font_small.render(WINNERS, True, (255, 255, 255)) winner_name_text = font_small.render(winners, True, (255, 255, 255)) screen_surface.blit( winner_text, result_pos ) screen_surface.blit( winner_name_text, (result_pos[0], result_pos[1] + winner_text.get_rect().height + 5) ) background_sprite = Sprite() background_sprite.rect = screen_surface.get_rect() background_sprite.image = screen_surface background_sprite.layer = 0 group.add(background_sprite) return group
class PlatformerScene(Scene): def __init__(self, game): super().__init__(game) self.player = None self.active = True self.geometry = list() self.space = pymunk.Space() self.space.gravity = (0, 1000) self.sprites = LayeredUpdates() self.event_handler = event_handling.EventQueueHandler() self.background = resources.gfx("background.png", convert=True) self.load() pygame.mixer.music.load(resources.music_path("zirkus.ogg")) pygame.mixer.music.play(-1) def add_static(self, vertices, rect): body = pymunk.Body(body_type=pymunk.Body.STATIC) body.position = rect.x, rect.y shape = pymunk.Poly(body, vertices) shape.friction = 1.0 shape.elasticity = 1.0 self.space.add(body, shape) def load(self): def box_vertices(x, y, w, h): lt = x, y rt = x + w, y rb = x + w, y + h lb = x, y + h return lt, rt, rb, lb filename = path_join("data", "maps", "untitled.tmx") tmxdata = pytmx.util_pygame.load_pygame(filename) for obj in tmxdata.objects: if obj.type == map_fixed: rect = Rect(obj.x, obj.y, obj.width, obj.height) vertices = box_vertices(0, 0, obj.width, obj.height) self.add_static(vertices, rect) elif obj.type == map_yarn_spawn: ball = sprite.Ball(Rect((obj.x, obj.y), (32, 32))) model = BasicModel() model.sprites = [ball] model.pymunk_objects = ball.pymunk_shapes self.add_model(model) self.player = model elif obj.type == map_player_spawn: self.player = unicyclecat.build(self.space, self.sprites) self.player.position = obj.x, obj.y self.fsm = SimpleFSM(control, "idle") def add_model(self, model): self.sprites.add(*model.sprites) self.space.add(model.pymunk_objects) def remove_model(self, model): self.sprites.remove(*model.sprites) self.space.remove(model.pymunk_objects) def render(self): surface = self._game.screen surface.blit(self.background, (0, 0)) self.sprites.draw(surface) return [surface.get_rect()] def tick(self, dt): step_amount = (1 / 30.) / 30 for i in range(30): self.space.step(step_amount) self.sprites.update(dt) def event(self, pg_event): events = self.event_handler.process_event(pg_event) position = self.player.position for event in events: try: cmd, arg = self.fsm((event.button, event.held)) except ValueError as e: continue if cmd == "move": resources.sfx("cat_wheel.ogg", False, True) resources.sfx("cat_wheel.ogg", True) self.player.accelerate(arg) if cmd == "idle": self.player.brake() elif cmd == "jump": resources.sfx("cat_jump.ogg", True) self.player.main_body.apply_impulse_at_world_point((0, -600), position)
class Cutscene(Minigame): GAME_NAME = "Cutscene" _default_layer = 1 def __init__(self, scene_name="cutscene001", scene_file_name="test-cutscene.yaml"): self.running = False self.script_runner = ScriptRunner() self._scene_name = scene_name self._scene_file_name = scene_file_name self._border = None self._sprites = LayeredUpdates() self._animations = Group() self._dialog_open = False self._dialog_rect = None self._caption = None self._text = None def initialize(self, context): with open(get_data_asset(self._scene_file_name)) as fp: config = yaml.load(fp) self.script_runner.start(self, config[self._scene_name]) @staticmethod def new_sprite(image, rect): sprite = Sprite() sprite.image = image sprite.rect = rect return sprite def animate(self, *args, **kwargs): ani = animation.Animation(*args, **kwargs) self._animations.add(ani) return ani def add_sprite(self, image, rect, layer=None): self._sprites.add(self.new_sprite(image, rect), layer=ternone(layer, self._default_layer)) def set_background(self, filename): surf = smoothscale(load_image(filename), SCREEN_SIZE).convert() rect = (0, 0), SCREEN_SIZE self.add_sprite(surf, rect, 0) def set_portrait(self, filename): size = 340, 680 # HACK to remove old portrait for sprite in self._sprites: if sprite.image.get_size() == size: self._sprites.remove(sprite) if filename is not None: surf = smoothscale(load_image(filename), size).convert_alpha() rect = (900, 60), size self.add_sprite(surf, rect, 1) def set_caption(self, value): if value is None: self._caption = None return get = self.script_runner.vars.get fcolor = Color(get('caption-fg', 'black')) font = load_font('pixChicago.ttf', 16) if 'caption-bg' in self.script_runner.vars and get( 'caption-bg') is not None: bcolor = Color(get('caption-bg')) image = font.render(value, 0, fcolor, bcolor) margins = image.get_rect().inflate(45, 0) background = pygame.Surface(margins.size) background.fill(bcolor) background.blit(image, (24, 0)) self._caption = background else: self._caption = font.render(value, 0, fcolor) def set_text(self, value): get = self.script_runner.vars.get fcolor = Color(get('text-fg', 'black')) bcolor = none_or_not(self.script_runner.vars, 'text-bg', Color) font = load_font('pixChicago.ttf', 16) w, h = self.final_rect().size w -= 48 final_rect = Rect((0, 0), (w, h)) self._text = pygame.Surface(final_rect.size, pygame.SRCALPHA) draw_text(self._text, value, final_rect, font, fcolor, bcolor) def final_rect(self): sw, sh = SCREEN_SIZE return Rect(sw * .05, sh * .6, sw * .62, sh * .35) def queue_dialog_text(self, value): self.set_text(value) def open_dialog(self): self._dialog_open = True final_rect = self.final_rect() self._dialog_rect = Rect(0, 0, 64, 64, center=final_rect.center) ani = self.animate(self._dialog_rect, height=final_rect.height, width=final_rect.width, duration=100) ani.schedule( lambda: setattr(self._dialog_rect, "center", final_rect.center), 'on update') def close_dialog(self): self._dialog_open = False def run(self, context): flip = pygame.display.flip update = self.update draw = self.draw handle_events = self.handle_event screen = pygame.display.get_surface() clock = time.time frame_time = (1 / 60.) * 1000 last_draw = 0 times = deque(maxlen=10) self.running = True last_frame = clock() while self.running: dt = (clock() - last_frame) * 1000 last_frame = clock() times.append(dt) dt = sum(times) / 10 last_draw += dt handle_events() update(dt) if last_draw >= frame_time: draw(screen) flip() last_draw = 0 pygame.mixer.music.fadeout(800) def draw(self, screen): self._sprites.draw(screen) if self._dialog_open: self.draw_dialog(screen) def draw_dialog(self, surface): with surface_clipping_context(surface, self._dialog_rect): self._border.draw(surface, self._dialog_rect) internal = self._dialog_rect.inflate(-48, -6) with surface_clipping_context(surface, internal): if self._caption: rect = self._caption.get_rect() rect.top = internal.top rect.centerx = internal.centerx surface.blit(self._caption, rect) if self._text: rect = internal.copy() rect.top = internal.top + 75 rect.left = internal.left surface.blit(self._text, rect) def update(self, dt): self._animations.update(dt) def button_press(self): """ Handles the ACTION button :return: """ self.script_runner.dialog_event('press') def handle_event(self): for event in pygame.event.get(): if event.type == KEYDOWN: if event.key == K_ESCAPE: self.running = False if event.key == K_SPACE: self.button_press() if event.type == QUIT: # this will allow pressing the windows (X) to close the game sys.exit(0)
class DDR(Microgame): def __init__(self): Microgame.__init__(self) self.rect=Rect(0,0, locals.WIDTH, locals.HEIGHT) self.arrow1=arrow(self,'left',50,50,0,0) self.arrow2=arrow(self,'down',150,50,0,0) self.arrow3=arrow(self,'up',250, 50,0,0) self.arrow4=arrow(self,'right',350,50,0,0) self.background=Background() self.sprites=LayeredUpdates([self.background,self.arrow1,self.arrow2,self.arrow3,self.arrow4]) self.timesincearrow=0 self.dancer=pyganim.PygAnimation([('games/DDR/DDRGIFS/TWERK/twerk1.png',0.1), ('games/DDR/DDRGIFS/TWERK/twerk2.png',0.1), ('games/DDR/DDRGIFS/TWERK/twerk3.png',0.1), ('games/DDR/DDRGIFS/TWERK/twerk4.png',0.1), ('games/DDR/DDRGIFS/TWERK/twerk5.png',0.1), ('games/DDR/DDRGIFS/TWERK/twerk6.png',0.1)]) self.anim=pyganim.PygAnimation([('games/DDR/DDRGIFS/MUSIC_ANIMATION/music1.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music2.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music3.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music4.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music5.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music6.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music7.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music8.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music9.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music10.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music11.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music12.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music13.png',0.1), ('games/DDR/DDRGIFS/MUSIC_ANIMATION/music14.png',0.1)]) self.disco=pyganim.PygAnimation([('games/DDR/DDRGIFS/DISCOBALL/disco3.png',0.1), ('games/DDR/DDRGIFS/DISCOBALL/disco4.png',0.1)]) self.dancer.play() self.anim.play() self.disco.play() def time_elapsed(self): self.timesincearrow=pygame.time.get_ticks() def start(self): self.time_elapsed() music.load('games/DDR/geekin.ogg') music.play(2,0.0) def stop(self): music.stop() def update(self): self.sprites.update() time=pygame.time.get_ticks() if time-self.timesincearrow>350: num=randint(0,3) if num==0: self.sprites.add(moving_arrow(self,'left',50,768,0,-10)) elif num==1: self.sprites.add(moving_arrow(self,'down',150,768,0,-10)) elif num==2: self.sprites.add(moving_arrow(self,'up',250,768,0,-10)) elif num==3: self.sprites.add(moving_arrow(self,'right',350,768,0,-10)) self.time_elapsed() for event in pygame.event.get(): if event.type==KEYDOWN or event.type==KEYUP: if event.key==K_q: self.lose() def render(self,surface): surface.fill(Color(0,0,0)) self.sprites.draw(surface) self.dancer.blit(surface,(800,100)) self.anim.blit(surface,(550,500)) self.disco.blit(surface,(600,50))
class Jackin(Minigame): GAME_NAME = "Jackin" _default_layer = 1 charset = "1234567890QWERTYUIOPASDFGHJKLZXCVBNM,.?>&/=" def __init__(self): super(Jackin, self).__init__() self.running = False self._sprites = LayeredUpdates() self._animations = Group() self.platten_speed = 40 self.pressed = set() self.cpl = 40 self.buffer = None self.cache = None self.char_height = 0 self.char_width = 0 self.platten = None self.paper = None self.mode = None self.fade_buffer = None self.screen_size = None self.cursor = Vector2(200, -70) self.document = list() self.document.append(list()) self.current_line = 0 font_name = 'Apple ][.ttf' font_size = 8 ratio = 10 color = pygame.Color('goldenrod') self.text = [ ('display', '> '), ('wait', 1500), ('type', 'CD/HACK'), ('display', '\nHACK> '), ('type', '\nRUN HACK.BAS'), ('wait', 1000), ('type', '\nAT&F&D2&C1S0=0X4'), ('wait', 1500), ('display', '\nOK'), ('wait', 1000), ('type', '\nATDT0040745129711\n'), ('sound', 'modem'), ('wait', 18000), ('display', '\nCONNECT 14400') ] self.sounds = { 'run': load_sound('run.wav'), 'boot': load_sound('boot.wav'), 'beep': load_sound('beep.wav'), 'modem': load_sound('188828__0ktober__modem-dial.wav'), 'spacebar': [load_sound(i) for i in [ 'spacebar01.wav', 'spacebar02.wav']], 'key': [load_sound(i) for i in [ 'keypress01.wav', 'keypress02.wav', 'keypress04.wav', 'keypress05.wav', 'keypress06.wav', ]] } self.generate_font(font_name, font_size, ratio, color) self.set_tab(0) def next_command(self): try: cmd, text = self.text.pop(0) except IndexError: self.fade_out() return if cmd == 'display': self.display(text) elif cmd == 'type': self.buffer = list(text) self.next_press() elif cmd == 'sound': self.sounds[text].play() self.next_command() elif cmd == 'wait': task = Task(self.next_command, text) self._animations.add(task) def next_press(self): try: self.press(self.buffer.pop(0)) except IndexError: self.next_command() else: if random.randint(0, 20): task = Task(self.next_press, interval=random.randint(200, 400)) else: task = Task(self.next_press, interval=random.randint(800, 1200)) self._animations.add(task) def fade_out(self): if self.fade_buffer is None: self.fade_buffer = pygame.Surface(self.screen_size) self.fade_buffer.fill(0) ani = self.animate(self.fade_buffer, set_alpha=255, initial=0, duration=3000) ani.schedule(partial(setattr, self, 'running', False), 'on finish') def display(self, text): for char in text: self.strike(char, True) task = Task(self.next_command, interval=random.randint(600, 800)) self._animations.add(task) def generate_font(self, font_name, font_size, ratio, color): self.cache = dict() padding = 8 padding2 = padding * 2 font = pygame.font.Font(get_font_asset(font_name), font_size) glyph = font.render('W', 1, (0, 0, 0)) natural_size = glyph.get_size() scratch = natural_size[0] + padding2, natural_size[1] + padding2 full_size = scratch[0] * ratio, scratch[1] * ratio # generate a scanline image to create scanline effect scanline = pygame.Surface(full_size, pygame.SRCALPHA) scolor = (64, 64, 64) for y in range(0, full_size[1], 4): pygame.draw.line(scanline, scolor, (0, y), (full_size[0], y)) pygame.draw.line(scanline, scolor, (0, y + 1), (full_size[0], y + 1)) for char in self.charset: original = font.render(char, 1, color) lined = pygame.Surface(natural_size, pygame.SRCALPHA) lined.blit(original, (0, 0)) lined.blit(scanline, (0, 0)) glyph = pygame.Surface(scratch, pygame.SRCALPHA) glyph.blit(original, (padding, padding)) large_glyph = smoothscale(glyph, full_size) # norml_glyph.blit(scanline, (0, 0)) bloom = smoothscale(glyph, (10, int(scratch[1] * .80))) bloom = smoothscale(bloom, full_size) image = pygame.Surface(full_size, pygame.SRCALPHA) image.blit(bloom, (0, 0)) image.blit(large_glyph, (0, 0)) image.blit(scanline, (0, 0), None, pygame.BLEND_SUB) self.cache[char] = image self.char_width = int(full_size[0] * .3) self.char_height = int(full_size[1] * .35) page_width = self.char_width * self.cpl self.platten = pygame.Rect(0, self.cursor.y, page_width, 100) self.paper = pygame.Rect(0, self.cursor.y, page_width, 1000) def initialize(self, context): pass @staticmethod def new_sprite(image, rect): sprite = Sprite() sprite.image = image sprite.rect = rect return sprite def animate(self, *args, **kwargs): ani = Animation(*args, **kwargs) self._animations.add(ani) return ani def add_sprite(self, image, rect, layer=None): self._sprites.add(self.new_sprite(image, rect), layer=ternone(layer, self._default_layer)) def run(self, context): flip = pygame.display.flip update = self.update draw = self.draw handle_events = self.handle_event screen = pygame.display.get_surface() clock = time.time frame_time = (1 / 60.) * 1000 last_draw = 0 times = deque(maxlen=10) self.sounds['boot'].play() self.sounds['run'].play(-1, fade_ms=200) task = Task(self.next_command, 2500) self._animations.add(task) self.screen_size = screen.get_size() self.running = True last_frame = clock() while self.running: dt = (clock() - last_frame) * 1000 last_frame = clock() times.append(dt) dt = sum(times) / 10 last_draw += dt handle_events() update(dt) if last_draw >= frame_time: draw(screen) if self.fade_buffer: screen.blit(self.fade_buffer, (0, 0)) flip() last_draw = 0 def draw(self, surface): self._sprites.draw(surface) surface.fill((0, 0, 0)) self.paper.left = self.platten.left for line_index, line in enumerate(self.document): for position, glyph in line: gx, gy = position x = gx y = gy + (line_index * self.char_height) surface.blit(glyph, self.paper.move(x, y)) def update(self, dt): self._animations.update(dt) def handle_event(self): for event in pygame.event.get(): if event.type == KEYDOWN: if event.key == K_SPACE: self.fade_out() if event.type == QUIT: # this will allow pressing the windows (X) to close the game sys.exit(0) def strike(self, char, now=False): self.paper.left = self.platten.left if char == ' ': self.advance_one(now) return elif char == '\n': self.cr() return try: glyph = self.cache[char] position = [int(self.cursor.x - self.paper.left), 10] self.document[self.current_line].append((position, glyph)) self.advance_one(now) return glyph except KeyError: pass def advance_one(self, now=False): if now: self.platten.left -= self.char_width else: remove_animations_of(self.platten, self._animations) ani = Animation(left=-self.char_width, relative=True, duration=self.platten_speed) ani.start(self.platten) self._animations.add(ani) def press(self, char, sound=80): if char in self.charset: if sound: random.choice(self.sounds['key']).play() task = Task(partial(self.strike, char), sound) elif char == ' ': if sound: random.choice(self.sounds['spacebar']).play() task = Task(self.advance_one, sound) elif char == '\n': if sound: random.choice(self.sounds['key']).play() task = Task(self.cr, sound) else: raise ValueError(char) # don't show character right away b/c sound lag self._animations.add(task) def release(self, key): """ :param key: :return: """ if key in self.pressed: self.pressed.remove(key) self.advance_one() def bs(self): """ Backspace :return: """ self.mode = 1 remove_animations_of(self.platten, self._animations) ani = Animation(left=self.char_width, relative=True, duration=self.platten_speed) ani.start(self.platten) self._animations.add(ani) def cr(self): """ Carriage return :return: """ self.current_line += 1 self.document.append(list()) self.set_tab(0) def set_tab(self, tab=0): self.platten.left = tab remove_animations_of(self.platten, self._animations) ani = Animation(left=0, duration=self.platten_speed) ani.start(self.platten) self._animations.add(ani)
class GraphView(Minigame): GAME_NAME = "GraphView" def __init__(self, graph_yaml=None, graph_tmx=None): self.visitor = None self.scroll_group = None self.graph_yaml = graph_yaml self.graph_tmx = graph_tmx self.visitor_cursor = None self.vertex_group = VertexLookupGroup() self.hud_group = None self.hud_button = None self.pointer = None self.sprites = LayeredUpdates() self._animations = Group() def animate(self, *args, **kwargs): ani = Animation(*args, **kwargs) self._animations.add(ani) return ani def initialize(self, context): screen = pygame.display.get_surface() adventure_graph = build_graph_from_yaml_data( load_yaml_data(get_data_asset(self.graph_yaml))) self.visitor = Visitor.visit_graph(adventure_graph, context) tmx_data = load_pygame(resources.get_map_asset(self.graph_tmx)) map_data = pyscroll.TiledMapData(tmx_data) map_layer_size = screen.get_width(), int(screen.get_height() * .80) map_layer_rect = Rect((0, 0), map_layer_size) map_layer = pyscroll.BufferedRenderer(map_data, map_layer_size) self.scroll_group = PyscrollGroup(map_layer=map_layer) self.pointer = PointerSprite(self.vertex_group, self.scroll_group) # self.pointer.selected_vertex_id = "START" self.sprites.add(self.pointer, layer=100) sw, sh = screen.get_size() hud_rect = Rect(20, sh * .76, sw * .80, sh * .2) border_image = load_image('border-default.png').convert_alpha() self.hud_group = HUDGroup(hud_rect, border_image) info = VertexInfoSprite(self.visitor) info.rect = Rect(12, 12, hud_rect.width - 250, hud_rect.height - 24) self.hud_group.add(info) self.hud_group.open() self.hud_button = HUDButton(self.hud_group, 850, 70) self.hud_group.add(self.hud_button) edges = list() for vertex in self.visitor.graph.vertex_index.values(): vertex_sprite = VertexSprite(vertex) self.vertex_group.add(vertex_sprite) self.scroll_group.add(vertex_sprite) for edge in vertex.edges: edges.append(edge) edge_sprites = dict() for edge in edges: key = [edge.from_vertex.vertex_id, edge.to_vertex.vertex_id] key.sort() key = tuple(key) if key not in edge_sprites: edge_sprite = EdgeSprite(edge, self.scroll_group) from_vertex_sprite = self.vertex_group[ edge.from_vertex.vertex_id] to_vertex_sprite = self.vertex_group[edge.to_vertex.vertex_id] from_vertex_sprite.edge_sprites.append(edge_sprite) to_vertex_sprite.edge_sprites.append(edge_sprite) edge_sprites[key] = edge_sprite self.visitor_cursor = VisitorCursor(self.visitor, self.pointer, self.vertex_group, self.scroll_group) if context.get('show_clippie', False): c = Clippie(self.sprites, self._animations, context) c.rect.topleft = 1100, 550 self.sprites.add(c) self.visitor_cursor.clippie = c clock = pygame.time.Clock() pygame.display.flip() pygame.mouse.set_visible(False) while True: delta = clock.tick(60) events = pygame.event.get() self.hud_group.update(delta, events) for event in events: if event.type == pygame.QUIT: sys.exit(0) elif event.type == pygame.KEYDOWN or self.hud_button.handle_click: vertex_id = self.visitor_cursor.current_vertex_sprite.vertex_id if self.hud_button.handle_click or pygame.K_SPACE == event.key and vertex_id == self.pointer.selected_vertex_id: self.hud_button.handle_click = False if self.visitor.current_vertex.can_activate( self.visitor.context): activation_dict = self.visitor.activate_current_vertex( ) if activation_dict[ "command"] == "launch-mini-game": r = RunMinigameActivation( activation_dict["command"], activation_dict["activation-keyword-args"]) logger.debug( "Stating game: {} with arguments {}". format(r.mini_game_name, r.mini_game_keyword_args)) unhandled_actions = self.minigame_manager.run_minigame( r.mini_game_name, self.visitor.context, r.post_run_actions, **r.mini_game_keyword_args if r.mini_game_keyword_args is not None else {}) if self.has_exit_action(unhandled_actions): return else: vertex = self.visitor.current_vertex failing = vertex.activation_pre_requisites.get_failing_pre_requisites( self.visitor.context) self.visitor.context[ 'gamestate.dialog_text'] = failing[0].hint self.visitor_cursor.animations.add( Task(self.visitor_cursor.clear_hint, 5000)) self.scroll_group.update(delta, events) # self.hud_group.update(delta, events) self.sprites.update(delta, events) self._animations.update(delta) self._update_edge_colors() x_offset = ( (self.visitor_cursor.rect.x - self.scroll_group.view.x) - self.pointer.rect.x) / 2 self.scroll_group.center( (self.visitor_cursor.rect.x - x_offset, 0)) self.scroll_group.draw(screen, map_layer_rect) screen.fill((200, 200, 200), (0, sh * .75, 1280, 200)) self.hud_group.draw(screen) self.sprites.draw(screen) screen.blit(self.pointer.image, self.pointer.rect) pygame.display.flip() def has_exit_action(self, unhandle_actions): for u in unhandle_actions: if u.ACTION_NAME == ExitGameAction.ACTION_NAME: return True return False def run(self, context): pass def _update_edge_colors(self): for key, vertex_sprite in self.vertex_group.lookup.items(): for e in vertex_sprite.edge_sprites: if e.edge.can_traverse(self.visitor.context): e.color = pygame.Color("GREEN")
class PlatformerScene(Scene): """ Platformer Scene class. """ def __init__(self, game): super().__init__(game) self.player = None self.active = True self.fsm = None self.space = pymunk.Space() self.space.gravity = (0, 1000) self.sprites = LayeredUpdates() self.event_handler = event_handling.EventQueueHandler() self.event_handler.print_controls() self.background = resources.gfx("background.png", convert=True) self.load() pygame.mixer.music.load(resources.music_path("zirkus.ogg")) pygame.mixer.music.play(-1) def add_static(self, vertices, rect): """ Add static object to scene. :param vertices: :param rect: """ body = pymunk.Body(body_type=pymunk.Body.STATIC) body.position = rect.x, rect.y shape = pymunk.Poly(body, vertices) shape.friction = 1.0 shape.elasticity = 1.0 self.space.add(body, shape) def load(self): """ Load a scene in TMX format. """ def box_vertices(box_x, box_y, width, height): top_left = box_x, box_y top_right = box_x + width, box_y bottom_right = box_x + width, box_y + height bottom_left = box_x, box_y + height return top_left, top_right, bottom_right, bottom_left filename = path_join("data", "maps", "untitled.tmx") tmxdata = pytmx.util_pygame.load_pygame(filename) for obj in tmxdata.objects: if obj.type == MAP_FIXED: rect = Rect(obj.x, obj.y, obj.width, obj.height) vertices = box_vertices(0, 0, obj.width, obj.height) self.add_static(vertices, rect) elif obj.type == MAP_YARN_SPAWN: ball = sprite.Ball(Rect((obj.x, obj.y), (32, 32))) model = BasicModel() model.sprites = [ball] model.pymunk_objects = ball.pymunk_shapes self.add_model(model) self.player = model elif obj.type == MAP_PLAYER_SPAWN: self.player = unicyclecat.build(self.space, self.sprites) self.player.position = obj.x, obj.y self.fsm = SimpleFSM(CONTROL, "idle") def add_model(self, model): """ Add a model. :param model: Model to add. """ self.sprites.add(*model.sprites) self.space.add(model.pymunk_objects) def remove_model(self, model): """ Remove a model. :param model: Model to remove. """ self.sprites.remove(*model.sprites) self.space.remove(model.pymunk_objects) def render(self): """ Render scene to the screen surface. """ surface = self._game.screen surface.blit(self.background, (0, 0)) self.sprites.draw(surface) return [surface.get_rect()] def tick(self, time_delta): """ Tick the physics and game update loops. """ step_amount = (1 / 30.0) / 30 for _ in range(30): self.space.step(step_amount) self.sprites.update(time_delta=time_delta) def event(self, event): """ Process an event. :param event: The event to process """ events = self.event_handler.process_event(event) position = self.player.position for evt in events: try: cmd, arg = self.fsm((evt.button, evt.held)) except ValueError: continue if cmd == "move": resources.sfx("cat_wheel.ogg", False, True) resources.sfx("cat_wheel.ogg", True) self.player.accelerate(arg) if cmd == "idle": self.player.brake() elif cmd == "jump": resources.sfx("cat_jump.ogg", True) self.player.main_body.apply_impulse_at_world_point((0, -600), position)