diff --git a/config b/config index ece27e8..2367d0a 100644 --- a/config +++ b/config @@ -1,7 +1,7 @@ [setup] title = Electric Sieve -url = http://A-O.in/ -contact-email = frank.s.demarco@gmail.com +url = https://shampoo.ooo +contact-email = mailbox@shampoo.ooo contact-name = Frank DeMarco summary = UFOs fracking your skull in 1920s America version = 0.1.1 @@ -18,6 +18,7 @@ title-font-path = font/Oxygen.ttf scoreboard-font-path = font/terminus/Terminus.ttf initials-font = font/terminus/Terminus-Bold.ttf show-framerate = yes +rotate = no [mouse] visible = no diff --git a/electric_sieve/ElectricSieve.py b/electric_sieve/ElectricSieve.py index 963ca0c..411cc8d 100644 --- a/electric_sieve/ElectricSieve.py +++ b/electric_sieve/ElectricSieve.py @@ -5,6 +5,7 @@ from random import randint, randrange, choice from time import time from operator import itemgetter +import pygame from pygame import Surface, PixelArray, Rect from pygame.draw import aalines, polygon from pygame.font import Font @@ -14,6 +15,7 @@ from pygame.locals import * from lib.pgfw.pgfw.Game import Game from lib.pgfw.pgfw.GameChild import GameChild from lib.pgfw.pgfw.Sprite import Sprite +from lib.pgfw.pgfw.Vector import Vector from lib.pgfw.pgfw.extension import render_box # Import GPIO library if available @@ -36,11 +38,27 @@ class ElectricSieve(Game): """ # Initialize super Game.__init__(self) + + # Add type declarations for custom config entries + self.get_configuration().type_declarations.add("bool", "display", "rotate") + + # Rotate the display if requested in the configuration + self.is_rotated = False + if self.get_configuration("display", "rotate"): + self.display.rotate() + self.is_rotated = True # Initialize GPIO input and callbacks if GPIO library is loaded if "RPi.GPIO" in sys.modules: self.initialize_gpio() + # Create game objects + self.title = Title(self) + self.sieve = Sieve(self) + self.triangles = Triangles(self) + self.acid = Acid(self) + self.static = Static(self) + # Create a purple background self.background = Surface(self.display.screen.get_size()) self.background.fill((255, 80, 190)) @@ -81,13 +99,55 @@ class ElectricSieve(Game): self.input.post_command("right", cancel=cancel) self.input.post_any_command(id=pin, cancel=cancel) - def set_children(self): - Game.set_children(self) - self.title = Title(self) - self.sieve = Sieve(self) - self.triangles = Triangles(self) - self.acid = Acid(self) - self.static = Static(self) + def rotated(self): + """ + @returns True if display has been rotated, False otherwise + """ + return self.is_rotated + + def orient(self, geometry, reference=None): + """ + Orient the passed pgfw.Vector, pygame.Rect, or pygame.Surface so it is rotated if necessary. + + @param geometry A pgfw.Vector or pygame.Rect to rotate + @return Either a new pgfw.Vector or pygame.Rect depending on which was passed + """ + if not self.rotated(): + return geometry + else: + if isinstance(geometry, Vector): + return self.rotated_point(geometry, reference) + elif isinstance(geometry, pygame.Rect): + return self.rotated_rect(geometry, reference) + elif isinstance(geometry, pygame.Surface): + return pygame.transform.rotate(geometry, 90) + + def rotated_point(self, point, reference=None): + """ + Return a new pgfw.Vector with the X and Y values of a pgfw.Vector rotated 90 degrees. + + @param point pgfw.Vector to rotate in place + @return rotated pgfw.Vector + """ + if reference is None: + reference = self.get_display_surface() + return Vector(reference.get_height() - point.x, point.y) + + def rotated_rect(self, rect, reference=None): + """ + Return a new pygame.Rect rotated 90 degrees. + + @param rect pygame.Rect to rotate in place + @return rotated pygame.Rect + """ + if reference is None: + reference = self.get_display_surface() + rotated = pygame.Rect(0, 0, 0, 0) + rotated.x = rect.y + rotated.y = reference.get_height() - rect.x + rect.w + rotated.w = rect.h + rotated.h = rect.w + return rotated def update(self): self.title.update() @@ -108,8 +168,8 @@ class Title(GameChild): self.background = surface = Surface(self.display_surface.get_size()) tile = Surface((2, 2)) tile.fill(bg_color) - tile.set_at((0, 1), (220, 119, 41)) - tile.set_at((1, 0), (220, 119, 41)) + tile.set_at(self.get_game().orient(Vector(0, 1)), (220, 119, 41)) + tile.set_at(self.get_game().orient(Vector(1, 0)), (220, 119, 41)) for y in range(0, surface.get_height(), 2): for x in range(0, surface.get_width(), 2): surface.blit(tile, (x, y)) @@ -208,7 +268,10 @@ class Strip(Sprite): for shift in self.hshifts: shift.update() if shift.time: - self.move(shift.get_change()) + if not self.get_game().rotated(): + self.move(shift.get_change()) + else: + self.move(dy=shift.get_change()) Sprite.update(self) @@ -228,11 +291,9 @@ class Shift(GameChild): def update(self): least, greatest = self.nodeset[0].x, self.nodeset[-1].x if self.active and self.time < greatest: - self.time = min(self.time + self.timer.get_last_frame_duration(), - greatest) + self.time = min(self.time + self.timer.get_last_frame_duration(), greatest) elif not self.active and self.time > least: - self.time = max(self.time - self.timer.get_last_frame_duration(), - least) + self.time = max(self.time - self.timer.get_last_frame_duration(), least) def get_change(self): return self.nodeset.get_y(self.time) * self.direction @@ -275,21 +336,28 @@ class Scoreboard(GameChild): surface.fill(self.FOREGROUND) rect.center = surface.get_rect().center surface.blit(score_plate, rect) - sprites[ii][1].add_frame(render_box(font, score_text, False, color, - self.BACKGROUND, self.FOREGROUND, self.BORDER, self.PADDING)) - sprites[ii][0].add_frame(render_box(font, score[2], False, color, - self.BACKGROUND, self.FOREGROUND, self.BORDER, self.PADDING)) + sprites[ii][1].add_frame(self.get_game().orient( + render_box(font, score_text, False, color, self.BACKGROUND, self.FOREGROUND, self.BORDER, self.PADDING))) + sprites[ii][0].add_frame(self.get_game().orient( + render_box(font, score[2], False, color, self.BACKGROUND, self.FOREGROUND, self.BORDER, self.PADDING))) if self.most_recent_score and not blink and score[1:] == self.most_recent_score: - sprites[ii][1].add_frame(render_box(font, score_text, False, self.NEW, - self.BACKGROUND, self.FOREGROUND, self.BORDER, self.PADDING)) - sprites[ii][0].add_frame(render_box(font, score[2], False, self.NEW, - self.BACKGROUND, self.FOREGROUND, self.BORDER, self.PADDING)) + sprites[ii][1].add_frame(self.get_game().orient( + render_box(font, score_text, False, self.NEW, self.BACKGROUND, self.FOREGROUND, self.BORDER, self.PADDING))) + sprites[ii][0].add_frame(self.get_game().orient( + render_box(font, score[2], False, self.NEW, self.BACKGROUND, self.FOREGROUND, self.BORDER, self.PADDING))) blink = True - sprites[ii][0].location.left = self.MARGIN - sprites[ii][1].location.right = self.get_display_surface().get_rect().right - self.MARGIN - y = self.get_display_surface().get_rect().centery + self.SPACING * (ii - len(sizes) / 2) - for sprite in sprites[ii]: - sprite.location.centery = y + if not self.get_game().rotated(): + sprites[ii][0].location.left = self.MARGIN + sprites[ii][1].location.right = self.get_display_surface().get_rect().right - self.MARGIN + y = self.get_display_surface().get_rect().centery + self.SPACING * (ii - len(sizes) / 2) + for sprite in sprites[ii]: + sprite.location.centery = y + else: + sprites[ii][0].location.bottom = self.get_display_surface().get_height() - self.MARGIN + sprites[ii][1].location.top = self.MARGIN + x = self.get_display_surface().get_rect().centerx + self.SPACING * (ii - len(sizes) / 2) + for sprite in sprites[ii]: + sprite.location.centerx = x def get_scores(self): scores = [] @@ -320,7 +388,12 @@ class Sieve(Strip): Strip.__init__(self, parent) self.delegate = self.get_game().delegate self.electric = Electric(self) - self.add_location(offset=(self.location.w, 0)) + if not self.get_game().rotated(): + self.location.left = 0 + self.add_location(offset=(self.location.w, 0)) + else: + self.location.bottom = self.get_display_surface().get_height() + self.add_location(offset=(0, -self.location.h)) def add_frames(self): bar_locations = [] @@ -346,26 +419,46 @@ class Sieve(Strip): for ii, frame in enumerate(frames): frame.fill(colors[ii], (x, 0, bar_w, sh)) frame.fill(colors[ii - 1], (x + 1, 1, 1, sh - 2)) + if self.get_game().rotated(): + for ii, rect in enumerate(bar_rects): + bar_rects[ii] = self.get_game().orient(rect) + bar_rects[ii].move_ip(0, -6) for frame in frames: - self.add_frame(frame) + self.add_frame(self.get_game().orient(frame)) def reset(self): Strip.reset(self) - self.location.centerx = self.display_surface.get_rect().centerx - self.locations[1].centerx = self.location.centerx + self.location.w + if not self.get_game().rotated(): + self.location.centerx = self.display_surface.get_rect().centerx + self.locations[1].centerx = self.location.centerx + self.location.w + else: + self.location.centery = self.display_surface.get_rect().centery + self.locations[1].centery = self.location.centery + self.location.h def update(self): if self.active: - if self.location.right < 0: - self.move(self.location.w) - if self.locations[1].left > self.display_surface.get_width(): - self.move(-self.location.w) - for location in self.locations: - location.bottom = self.parent.acid.get_top() - self.electric.location.centery = self.location.centery + 13 + if not self.get_game().rotated(): + if self.location.right < 0: + self.move(self.location.w) + if self.locations[1].left > self.display_surface.get_width(): + self.move(-self.location.w) + for location in self.locations: + location.bottom = self.parent.acid.get_top() + self.electric.location.centery = self.location.centery + 13 + else: + if self.location.top > self.display_surface.get_height(): + self.move(dy=-self.location.h) + if self.locations[1].bottom < 0: + self.move(dy=self.location.h) + for location in self.locations: + location.right = self.parent.acid.get_top() + self.electric.location.centerx = self.location.centerx + 13 self.electric.update() for rect in self.bar_rects: - rect.centery = self.location.centery + if not self.get_game().rotated(): + rect.centery = self.location.centery + else: + rect.centerx = self.location.centerx Strip.update(self) @@ -377,8 +470,10 @@ class Electric(Sprite): self.add_frames() def add_frames(self): - surface = Surface((self.display_surface.get_width(), - self.parent.location.h - 10)) + if not self.get_game().rotated(): + surface = Surface((self.display_surface.get_width(), self.parent.location.h - 10)) + else: + surface = Surface((self.display_surface.get_height(), self.parent.location.w - 10)) frames = surface, surface.copy() colors = (255, 255, 0), (100, 89, 213) pixel_arrays = PixelArray(frames[0]), PixelArray(frames[1]) @@ -389,7 +484,7 @@ class Electric(Sprite): for pixels in pixel_arrays: del pixels for frame in frames: - self.add_frame(frame) + self.add_frame(self.get_game().orient(frame)) class Triangles(GameChild, list): @@ -419,12 +514,21 @@ class Triangles(GameChild, list): def populate(self): if not self: self.append(Triangle(self)) - self[-1].location.bottom = 0 - self.set_next_gap() - while self[-1].location.top > -self.display_surface.get_height(): - self.append(Triangle(self)) - self[-1].location.bottom = self[-2].location.top - self.next_gap + if not self.get_game().rotated(): + self[-1].location.bottom = 0 + else: + self[-1].location.right = 0 self.set_next_gap() + if not self.get_game().rotated(): + while self[-1].location.top > -self.display_surface.get_height(): + self.append(Triangle(self)) + self[-1].location.bottom = self[-2].location.top - self.next_gap + self.set_next_gap() + else: + while self[-1].location.left > -self.display_surface.get_width(): + self.append(Triangle(self)) + self[-1].location.right = self[-2].location.left - self.next_gap + self.set_next_gap() def set_next_gap(self): self.next_gap = randint(500, 800) @@ -451,17 +555,17 @@ class Triangles(GameChild, list): if self[0].location.colliderect(sieve.electric.location): self.parent.acid.increase() self.streak += 1 - self.score += self.streak ** .8 + \ - self.parent.acid.get_volume() * 5 + \ - self[0].count + self.score += self.streak ** .8 + self.parent.acid.get_volume() * 5 + self[0].count self.remove(self[0]) self.hit.play() else: for br in sieve.bar_rects: for tr in self[0].collision_rects: - if tr.move((self[0].location.left, - 0)).colliderect(br.move((sieve.location.left, - 0))): + tr_offset = (self[0].location.left, 0) if not self.get_game().rotated() else \ + (0, self[0].location.bottom - self.get_display_surface().get_height()) + br_offset = (sieve.location.left, 0) if not self.get_game().rotated() else \ + (0, sieve.location.bottom - self.get_display_surface().get_height()) + if tr.move(tr_offset).colliderect(br.move(br_offset)): self.remove(self[0]) self.parent.static.increase() self.streak = 0 @@ -490,21 +594,33 @@ class Triangle(Sprite): self.collision_rects = collision_rects = [] for width in widths: x += sieve.bar_w - points = (x + margin / 2, height - 2), \ - (x + width - margin / 2 - 1, height - 2), \ + points = (x + margin // 2, height - 2), \ + (x + width - margin // 2 - 1, height - 2), \ (x + width / 2.0, 1) polygon(surface, (60, 255, 220), points) - collision_rects.append(Rect(points[0], (width - margin - 1, 1))) + if not self.get_game().rotated(): + collision_rects.append(Rect(points[0], (width - margin - 1, 1))) + else: + collision_rects.append(Rect(height - 2 - 1, self.get_display_surface().get_height() - x - width + margin // 2 + 1, 1, width - margin - 1)) x += width - sieve.bar_w - self.add_frame(surface) - self.location.centerx = self.get_display_surface().get_rect().centerx + self.add_frame(self.get_game().orient(surface)) + if not self.get_game().rotated(): + self.location.centerx = self.get_display_surface().get_rect().centerx + else: + self.location.centery = self.get_display_surface().get_rect().centery self.count = len(widths) def update(self): - self.move(dy=9.5 * self.get_game().acid.get_volume() + 3.8 + \ - self.parent.get_boost()) + step = 9.5 * self.get_game().acid.get_volume() + 3.8 + self.parent.get_boost() + if not self.get_game().rotated(): + self.move(dy=step) + else: + self.move(dx=step) for rect in self.collision_rects: - rect.bottom = self.location.bottom + if not self.get_game().rotated(): + rect.bottom = self.location.bottom + else: + rect.right = self.location.right Sprite.update(self) @@ -521,11 +637,13 @@ class Acid(GameChild): self.substance = 0 def get_top(self): - return self.display_surface.get_height() - self.get_level() + if not self.get_game().rotated(): + return self.display_surface.get_height() - self.get_level() + else: + return self.display_surface.get_width() - self.get_level() def get_level(self): - return self.get_volume() * (self.level_r[1] - self.level_r[0]) + \ - self.level_r[0] + return self.get_volume() * (self.level_r[1] - self.level_r[0]) + self.level_r[0] def get_volume(self): return self.nodeset.get_y(self.substance) @@ -646,7 +764,6 @@ class Initials(GameChild): PADDING = 10 ARROW_MARGIN = 40 ARROW_HEIGHT = 10 - def __init__(self, parent): GameChild.__init__(self, parent) @@ -703,22 +820,27 @@ class Initials(GameChild): if self.active: ds = self.get_display_surface() for ii, letter in enumerate(self.text): - box = render_box(self.font, letter, False, self.FOREGROUND, self.BACKGROUND, - self.FOREGROUND, padding=self.PADDING) + box = self.get_game().orient(render_box( + self.font, letter, False, self.FOREGROUND, self.BACKGROUND, self.FOREGROUND, padding=self.PADDING)) rect = box.get_rect() - rect.centery = ds.get_rect().centery - rect.centerx = ii * ds.get_width() / 3 + ds.get_width() / 6 + if not self.get_game().rotated(): + rect.centery = ds.get_rect().centery + rect.centerx = ii * ds.get_width() / 3 + ds.get_width() / 6 + else: + rect.centerx = ds.get_rect().centerx + rect.centery = (len(self.text) - 1 - ii) * ds.get_height() / 3 + ds.get_height() / 6 ds.blit(box, rect) if ii == self.index: - margin = self.ARROW_MARGIN + if not self.get_game().rotated(): + margin = self.ARROW_MARGIN + else: + margin = self.ARROW_MARGIN // 2 polygon(ds, (0, 255, 0), ((rect.left, rect.top - margin), (rect.right, rect.top - margin), - (rect.centerx, - rect.top - margin - self.ARROW_HEIGHT))) + (rect.centerx, rect.top - margin - self.ARROW_HEIGHT))) polygon(ds, (0, 255, 0), ((rect.left, rect.bottom + margin), (rect.right, rect.bottom + margin), - (rect.centerx, - rect.bottom + margin + self.ARROW_HEIGHT))) + (rect.centerx, rect.bottom + margin + self.ARROW_HEIGHT))) class Total(Sprite): @@ -763,7 +885,7 @@ class Total(Sprite): # polygon(surface, choice(colors), ((tr.centerx - 7, 80), # (tr.centerx, tr.h - 1), # (tr.centerx + 7, 80))) - self.add_frame(surface) + self.add_frame(self.get_game().orient(surface)) self.location.center = self.display_surface.get_rect().center self.active = True diff --git a/lib/pgfw b/lib/pgfw index 24b5d7c..7a16527 160000 --- a/lib/pgfw +++ b/lib/pgfw @@ -1 +1 @@ -Subproject commit 24b5d7c6e7c7f17ec09ddb3de2127c092aa6f874 +Subproject commit 7a1652717931d49c112e8fe75919129b4906d9f4 diff --git a/resource/high-scores b/resource/high-scores index 0f0582f..d73084f 100644 --- a/resource/high-scores +++ b/resource/high-scores @@ -134,3 +134,13 @@ 1670819531.720299 203 FLA 1670819619.775536 96 --- 1670989457.377776 0 --- +1671076263.7870994 4 --- +1671076281.177432 0 --- +1671076361.7916923 20 --- +1671076398.698485 4 --- +1671076453.8790646 43 --- +1671076466.5137062 0 --- +1671076561.3319085 52 --- +1671086208.2518148 135 --- +1671161829.4724848 14 --- +1671168164.8205705 0 ---