diff --git a/NS.py b/NS.py index 95ff76c..b726e64 100644 --- a/NS.py +++ b/NS.py @@ -37,7 +37,7 @@ from lib.pgfw.pgfw.Animation import Animation from lib.pgfw.pgfw.extension import ( get_step, get_step_relative, get_delta, reflect_angle, render_box, get_hsla_color, get_hue_shifted_surface, - get_color_swapped_surface + get_color_swapped_surface, load_frames ) from lib.pgfw.pgfw.gfx_extension import aa_filled_polygon @@ -56,10 +56,10 @@ class NS(Game, Animation): # four sides of the square and the two diagonals. N, NE, E, NW, S, W = range(6) - FRONT_WIDTH = 230 - BACK_WIDTH = 500 - LENGTH = 150 - FRONT = 300 + FRONT_WIDTH = 156 + BACK_WIDTH = 271 + LENGTH = 94 + FRONT = 330 STEP = .4 IDLE_TIMEOUT = 60000 * 5 CHANNEL_COUNT = 8 @@ -101,6 +101,10 @@ class NS(Game, Animation): "system": { "bool": "minimize-load-time" + }, + "pads": + { + "int-list": "center" } }) # If a serial port was passed on the command line, override the config file setting @@ -497,6 +501,7 @@ class Tony(Sprite): self.effect.update(flags=BLEND_RGBA_SUB) self.get_display_surface().blit(intermediate_surface, self.location.topleft) if self.get_game().title.active: + self.get_game().title.video.update() self.get_game().platform.update() self.get_game().chemtrails.update() frameset = self.get_current_frameset() @@ -718,7 +723,7 @@ class Title(Animation): self.get_game().tony.set_frameset("static") self.halt() self.play(self.show_video, delay=self.get_configuration("time", "attract-reset-countdown"), play_once=True) - self.video.update() + # self.video.update() self.draw_scores() @@ -1000,21 +1005,56 @@ class Wipe(Animation): class Platform(GameChild): + """ + This class contains methods for manipulating and getting information about the platform the player is standing on, + both the real one and on-screen representation. It initializes four Light objects, one for each pad on the platform. + It can set lights to glowing, return the states of individual lights or pairs of lights, reset lights, and draw the + on-screen representation. + """ def __init__(self, parent): + """ + Initialize four lights, one for each pad on the platform. Initialize a Sprite for the pad graphics with one + frameset per six possible combinations of lights. Initialize masks for creating a glow effect on the pads. + """ GameChild.__init__(self, parent) - dsr = self.get_display_surface().get_rect() - self.border = Sprite(self) - self.border.load_from_path(self.get_resource("DancePadClear.png"), True) - self.border.location.midbottom = dsr.centerx, dsr.bottom self.lights = [ Light(self, self.get_configuration("pads", "nw_color"), NS.LNW), Light(self, self.get_configuration("pads", "ne_color"), NS.LNE), Light(self, self.get_configuration("pads", "se_color"), NS.LSE), Light(self, self.get_configuration("pads", "sw_color"), NS.LSW) ] + self.view = Sprite(self) + self.view.load_from_path("pad", True) + self.view.add_frameset([0], name="neutral") + self.view.add_frameset([1], name=str(NS.N)) + self.view.add_frameset([2], name=str(NS.E)) + self.view.add_frameset([3], name=str(NS.NW)) + self.view.add_frameset([4], name=str(NS.NE)) + self.view.add_frameset([5], name=str(NS.W)) + self.view.add_frameset([6], name=str(NS.S)) + self.view.location.center = self.get_configuration("pads", "center") + self.glow_masks = [] + base_images = load_frames(self.get_resource("pad_mask"), True) + for image in base_images: + self.glow_masks.append([image]) + for mask in self.glow_masks: + intensity_resolution = 12 + for intensity in range(1, intensity_resolution): + copy = mask[0].copy() + pixels = pygame.PixelArray(copy) + color = pygame.Color(0, 0, 0) + h, s, l, a = color.hsla + l = int(intensity / intensity_resolution * 100) + color.hsla = h, s, l, a + pixels.replace(pygame.Color(0, 0, 0), color) + del pixels + mask.append(copy) def reset(self): + """ + Deactivate this object and reset each light + """ self.deactivate() self.reset_lights() @@ -1023,19 +1063,37 @@ class Platform(GameChild): light.reset() def deactivate(self): + """ + This will stop the platform from being drawn and lights from updating + """ self.active = False def activate(self): + """ + This will cause the platform to get drawn and lights to update when this object's update method is called + """ self.active = True def unpress(self): + """ + Set the state of each light to unpressed + """ for light in self.lights: light.pressed = False def get_pressed(self): + """ + Returns a list of light positions pressed (NS.LNW, NS.LNE, NS.LSE, NS.LSW) + """ return [light.position for light in self.lights if light.pressed] def get_edge_pressed(self): + """ + Gets the edge (2 light combination) currently pressed. This only returns one edge since there should only + be one able to be pressed at a time. If no edge is pressed, returns None. + + @return NS.N | NS.NE | NS.E | NS.NW | NS.S | NS.W | None + """ pressed = self.get_pressed() if NS.LNW in pressed and NS.LNE in pressed: return NS.N @@ -1050,7 +1108,32 @@ class Platform(GameChild): elif NS.LSW in pressed and NS.LNW in pressed: return NS.W + def get_glowing_edge(self): + """ + Return the edge currently glowing or None + + @return NS.N | NS.NE | NS.E | NS.NW | NS.S | NS.W | None + """ + if self.lights[NS.LNW].glowing() and self.lights[NS.LNE].glowing(): + return NS.N + elif self.lights[NS.LNE].glowing() and self.lights[NS.LSW].glowing(): + return NS.NE + elif self.lights[NS.LNE].glowing() and self.lights[NS.LSE].glowing(): + return NS.E + elif self.lights[NS.LNW].glowing() and self.lights[NS.LSE].glowing(): + return NS.NW + elif self.lights[NS.LSE].glowing() and self.lights[NS.LSW].glowing(): + return NS.S + elif self.lights[NS.LSW].glowing() and self.lights[NS.LNW].glowing(): + return NS.W + def get_buttons_from_edges(self, edges): + """ + Get a list of light positions contained by a list of edges. For example, [NS.N, NS.E] would give [NS.LNW, NS.LNE, NS.LSE]. + + @param edges list of edges [NS.N | NS.NE | NS.E | NS.NW | NS.S | NS.W] + @return list of light positions [NS.LNW | NS.LNE | NS.LSE | NS.LSW] + """ buttons = set() for edge in edges: if edge == NS.N: @@ -1068,6 +1151,13 @@ class Platform(GameChild): return list(buttons) def get_steps_from_edge(self, edge): + """ + Get the edges that are one step away from a given edge. For example, NS.N would give (NS.NE, NS.NW) because those + are the edges that only require a pivot move of one step from NS.N. + + @param edge one of NS.N, NS.NE, NS.E, NS.NW, NS.S, NS.W + @return pair of edges that are one step away + """ if edge == NS.N: return NS.NE, NS.NW elif edge == NS.NE: @@ -1082,6 +1172,13 @@ class Platform(GameChild): return NS.NE, NS.NW def get_right_angles_from_edge(self, edge): + """ + Get the pair of angles that are at a right angle to a given edge. For example, NS.N would return (NS.E, NW.W). For + diagonals, this returns None. + + @param edge one of NS.N, NS.NE, NS.E, NS.NW, NS.S, NS.W + @return pair of edges that are at a right angle to given edge or None + """ if edge == NS.N: return NS.E, NS.W elif edge == NS.NE: @@ -1096,6 +1193,13 @@ class Platform(GameChild): return NS.N, NS.S def get_opposite_of_edge(self, edge): + """ + Get the edge opposite to a given edge. For example, NS.N would return NS.S. For diagonals, the opposite is the + reverse diagonal. + + @param edge one of NS.N, NS.NE, NS.E, NS.NW, NS.S, NS.W + @return edge opposite to given edge, one of NS.N, NS.NE, NS.E, NS.NW, NS.S, NS.W + """ if edge == NS.N: return NS.S elif edge == NS.NE: @@ -1110,6 +1214,12 @@ class Platform(GameChild): return NS.E def get_color_pair_from_edge(self, edge): + """ + Return the pair of pygame color objects that make up a given edge + + @param edge one of NS.N, NS.NE, NS.E, NS.NW, NS.S, NS.W + @return tuple of pygame color objects + """ if edge == NS.N: return self.lights[NS.LNW].color, self.lights[NS.LNE].color elif edge == NS.NE: @@ -1124,6 +1234,11 @@ class Platform(GameChild): return self.lights[NS.LNW].color, self.lights[NS.LSW].color def set_glowing(self, selected): + """ + Set the given light IDs to glowing and other indices to not glowing. + + @param selected list of light IDs (NS.LNW, NS.LNE, NS.LSE, NS.LSW) + """ for ii, light in enumerate(self.lights): light.glow_index = 0 light.halt(light.glow) @@ -1131,20 +1246,44 @@ class Platform(GameChild): light.play(light.glow) def update(self): + """ + Update each light and draw the platform and glow effect + """ if self.active: for light in self.lights: light.update() - # self.border.update() - for light in self.lights: - light.draw_glow() + # draw the pad based on which pads are glowing + glowing = self.get_glowing_edge() + if glowing is None: + self.view.set_frameset("neutral") + self.view.update() + else: + self.view.set_frameset(str(glowing)) + self.view.update() + for light in self.lights: + if light.glowing(): + self.get_display_surface().blit(self.glow_masks[light.position][light.glow_index], self.view.location, None, BLEND_RGBA_ADD) + # for light in self.lights: + # light.draw_glow() class Light(Animation): + """ + This class represents a pad on the platform. Typically there are four instances for a platform, one for each corner of the + platform. Each light stores its color and position on the platform. This class contains methods for glowing the light and + getting its properties. + """ - MAX_GLOW_INDEX = 25 TITLE_OFFSET = 0 def __init__(self, parent, color, position): + """ + Initialize a new Light object, providing color and position on the platform. + + @param parent PGFW game object that instantiated this object + @param color pygame color object + @param position the light's position on the platform, one of NS.LNW, NS.LNE, NS.LSE, NS.LSW + """ Animation.__init__(self, parent) self.color = Color(color) self.color.a = 225 @@ -1170,38 +1309,43 @@ class Light(Animation): self.points = mid, midright, backright, backmid elif self.position == NS.LSW: self.points = midleft, mid, backmid, backleft - self.register(self.blink, interval=300) self.register(self.glow) def reset(self): + """ + Unhide, halt glow animation + """ self.hidden = False - self.halt(self.blink) self.halt(self.glow) self.reset_timer() self.glow_index = 0 - def blink(self): - self.hidden = not self.hidden - def glow(self): + """ + Moves the glow animation forward a frame by incrementing an index + """ self.glow_index += 1 - if self.glow_index > self.MAX_GLOW_INDEX: + if self.glow_index >= len(self.parent.glow_masks[0]): self.glow_index = 0 def update(self): + """ + Checks the attack state to determine whether to start or stop glowing + """ Animation.update(self) if not self.get_game().title.active: boss = self.get_game().boss chemtrails = self.get_game().chemtrails + # checks the boss attack queue and chameleon queue index to see if the glow should be started now if boss.queue and boss.brandish_complete and not self.is_playing(self.glow) \ and self.in_orientation(boss.queue[chemtrails.queue_index]): self.play(self.glow) - elif self.is_playing(self.glow) and (not boss.queue or - not self.in_orientation(boss.queue[chemtrails.queue_index])): + # turns off the glow + elif self.is_playing(self.glow) and (not boss.queue or not self.in_orientation(boss.queue[chemtrails.queue_index])): self.reset() - if not self.hidden: - ds = self.get_display_surface() - aa_filled_polygon(ds, self.get_points(), self.color) + # if not self.hidden: + # ds = self.get_display_surface() + # aa_filled_polygon(ds, self.get_points(), self.color) def get_points(self): if self.get_game().title.active: @@ -1234,6 +1378,12 @@ class Light(Animation): ) def in_orientation(self, orientation): + """ + Returns True if this light is contained in the given edge + + @param orientation edge to check, one of NS.N, NS.NW, NS.W, NS.NE, NS.E, NS.S + @return True | False + """ if self.position == NS.LNW: return orientation in (NS.N, NS.NW, NS.W) elif self.position == NS.LNE: @@ -1243,6 +1393,14 @@ class Light(Animation): elif self.position == NS.LSW: return orientation in (NS.S, NS.NE, NS.W) + def glowing(self): + """ + Returns True if this light is glowing, False otherwise + + @return True | False + """ + return self.is_playing(self.glow) + class Chemtrails(Sprite): diff --git a/config b/config index a4c3db7..6ebc4f5 100644 --- a/config +++ b/config @@ -70,3 +70,4 @@ nw_color = #00FF88 ne_color = #FF88FF se_color = #2222FF sw_color = #FF2222 +center = 319, 376 diff --git a/resource/pad/pad_0.png b/resource/pad/pad_0.png new file mode 100644 index 0000000..02a6ce5 Binary files /dev/null and b/resource/pad/pad_0.png differ diff --git a/resource/pad_mask/pad_mask_0.png b/resource/pad_mask/pad_mask_0.png new file mode 100644 index 0000000..23f1272 Binary files /dev/null and b/resource/pad_mask/pad_mask_0.png differ diff --git a/resource/pad_mask/pad_mask_1.png b/resource/pad_mask/pad_mask_1.png new file mode 100644 index 0000000..ce39f70 Binary files /dev/null and b/resource/pad_mask/pad_mask_1.png differ diff --git a/resource/pad_mask/pad_mask_2.png b/resource/pad_mask/pad_mask_2.png new file mode 100644 index 0000000..8ec0b53 Binary files /dev/null and b/resource/pad_mask/pad_mask_2.png differ diff --git a/resource/pad_mask/pad_mask_3.png b/resource/pad_mask/pad_mask_3.png new file mode 100644 index 0000000..db32efc Binary files /dev/null and b/resource/pad_mask/pad_mask_3.png differ