new pad graphics and glow

This commit is contained in:
frank 2022-02-24 01:16:45 -05:00
parent 0526507649
commit 607f6a8059
7 changed files with 184 additions and 25 deletions

208
NS.py
View File

@ -37,7 +37,7 @@ from lib.pgfw.pgfw.Animation import Animation
from lib.pgfw.pgfw.extension import ( from lib.pgfw.pgfw.extension import (
get_step, get_step_relative, get_delta, reflect_angle, get_step, get_step_relative, get_delta, reflect_angle,
render_box, get_hsla_color, get_hue_shifted_surface, 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 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. # four sides of the square and the two diagonals.
N, NE, E, NW, S, W = range(6) N, NE, E, NW, S, W = range(6)
FRONT_WIDTH = 230 FRONT_WIDTH = 156
BACK_WIDTH = 500 BACK_WIDTH = 271
LENGTH = 150 LENGTH = 94
FRONT = 300 FRONT = 330
STEP = .4 STEP = .4
IDLE_TIMEOUT = 60000 * 5 IDLE_TIMEOUT = 60000 * 5
CHANNEL_COUNT = 8 CHANNEL_COUNT = 8
@ -101,6 +101,10 @@ class NS(Game, Animation):
"system": "system":
{ {
"bool": "minimize-load-time" "bool": "minimize-load-time"
},
"pads":
{
"int-list": "center"
} }
}) })
# If a serial port was passed on the command line, override the config file setting # 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.effect.update(flags=BLEND_RGBA_SUB)
self.get_display_surface().blit(intermediate_surface, self.location.topleft) self.get_display_surface().blit(intermediate_surface, self.location.topleft)
if self.get_game().title.active: if self.get_game().title.active:
self.get_game().title.video.update()
self.get_game().platform.update() self.get_game().platform.update()
self.get_game().chemtrails.update() self.get_game().chemtrails.update()
frameset = self.get_current_frameset() frameset = self.get_current_frameset()
@ -718,7 +723,7 @@ class Title(Animation):
self.get_game().tony.set_frameset("static") self.get_game().tony.set_frameset("static")
self.halt() self.halt()
self.play(self.show_video, delay=self.get_configuration("time", "attract-reset-countdown"), play_once=True) 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() self.draw_scores()
@ -1000,21 +1005,56 @@ class Wipe(Animation):
class Platform(GameChild): 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): 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) 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 = [ self.lights = [
Light(self, self.get_configuration("pads", "nw_color"), NS.LNW), 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", "ne_color"), NS.LNE),
Light(self, self.get_configuration("pads", "se_color"), NS.LSE), Light(self, self.get_configuration("pads", "se_color"), NS.LSE),
Light(self, self.get_configuration("pads", "sw_color"), NS.LSW) 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): def reset(self):
"""
Deactivate this object and reset each light
"""
self.deactivate() self.deactivate()
self.reset_lights() self.reset_lights()
@ -1023,19 +1063,37 @@ class Platform(GameChild):
light.reset() light.reset()
def deactivate(self): def deactivate(self):
"""
This will stop the platform from being drawn and lights from updating
"""
self.active = False self.active = False
def activate(self): 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 self.active = True
def unpress(self): def unpress(self):
"""
Set the state of each light to unpressed
"""
for light in self.lights: for light in self.lights:
light.pressed = False light.pressed = False
def get_pressed(self): 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] return [light.position for light in self.lights if light.pressed]
def get_edge_pressed(self): 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() pressed = self.get_pressed()
if NS.LNW in pressed and NS.LNE in pressed: if NS.LNW in pressed and NS.LNE in pressed:
return NS.N return NS.N
@ -1050,7 +1108,32 @@ class Platform(GameChild):
elif NS.LSW in pressed and NS.LNW in pressed: elif NS.LSW in pressed and NS.LNW in pressed:
return NS.W 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): 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() buttons = set()
for edge in edges: for edge in edges:
if edge == NS.N: if edge == NS.N:
@ -1068,6 +1151,13 @@ class Platform(GameChild):
return list(buttons) return list(buttons)
def get_steps_from_edge(self, edge): 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: if edge == NS.N:
return NS.NE, NS.NW return NS.NE, NS.NW
elif edge == NS.NE: elif edge == NS.NE:
@ -1082,6 +1172,13 @@ class Platform(GameChild):
return NS.NE, NS.NW return NS.NE, NS.NW
def get_right_angles_from_edge(self, edge): 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: if edge == NS.N:
return NS.E, NS.W return NS.E, NS.W
elif edge == NS.NE: elif edge == NS.NE:
@ -1096,6 +1193,13 @@ class Platform(GameChild):
return NS.N, NS.S return NS.N, NS.S
def get_opposite_of_edge(self, edge): 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: if edge == NS.N:
return NS.S return NS.S
elif edge == NS.NE: elif edge == NS.NE:
@ -1110,6 +1214,12 @@ class Platform(GameChild):
return NS.E return NS.E
def get_color_pair_from_edge(self, edge): 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: if edge == NS.N:
return self.lights[NS.LNW].color, self.lights[NS.LNE].color return self.lights[NS.LNW].color, self.lights[NS.LNE].color
elif edge == NS.NE: elif edge == NS.NE:
@ -1124,6 +1234,11 @@ class Platform(GameChild):
return self.lights[NS.LNW].color, self.lights[NS.LSW].color return self.lights[NS.LNW].color, self.lights[NS.LSW].color
def set_glowing(self, selected): 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): for ii, light in enumerate(self.lights):
light.glow_index = 0 light.glow_index = 0
light.halt(light.glow) light.halt(light.glow)
@ -1131,20 +1246,44 @@ class Platform(GameChild):
light.play(light.glow) light.play(light.glow)
def update(self): def update(self):
"""
Update each light and draw the platform and glow effect
"""
if self.active: if self.active:
for light in self.lights: for light in self.lights:
light.update() light.update()
# self.border.update() # draw the pad based on which pads are glowing
for light in self.lights: glowing = self.get_glowing_edge()
light.draw_glow() 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): 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 TITLE_OFFSET = 0
def __init__(self, parent, color, position): 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) Animation.__init__(self, parent)
self.color = Color(color) self.color = Color(color)
self.color.a = 225 self.color.a = 225
@ -1170,38 +1309,43 @@ class Light(Animation):
self.points = mid, midright, backright, backmid self.points = mid, midright, backright, backmid
elif self.position == NS.LSW: elif self.position == NS.LSW:
self.points = midleft, mid, backmid, backleft self.points = midleft, mid, backmid, backleft
self.register(self.blink, interval=300)
self.register(self.glow) self.register(self.glow)
def reset(self): def reset(self):
"""
Unhide, halt glow animation
"""
self.hidden = False self.hidden = False
self.halt(self.blink)
self.halt(self.glow) self.halt(self.glow)
self.reset_timer() self.reset_timer()
self.glow_index = 0 self.glow_index = 0
def blink(self):
self.hidden = not self.hidden
def glow(self): def glow(self):
"""
Moves the glow animation forward a frame by incrementing an index
"""
self.glow_index += 1 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 self.glow_index = 0
def update(self): def update(self):
"""
Checks the attack state to determine whether to start or stop glowing
"""
Animation.update(self) Animation.update(self)
if not self.get_game().title.active: if not self.get_game().title.active:
boss = self.get_game().boss boss = self.get_game().boss
chemtrails = self.get_game().chemtrails 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) \ if boss.queue and boss.brandish_complete and not self.is_playing(self.glow) \
and self.in_orientation(boss.queue[chemtrails.queue_index]): and self.in_orientation(boss.queue[chemtrails.queue_index]):
self.play(self.glow) self.play(self.glow)
elif self.is_playing(self.glow) and (not boss.queue or # turns off the glow
not self.in_orientation(boss.queue[chemtrails.queue_index])): elif self.is_playing(self.glow) and (not boss.queue or not self.in_orientation(boss.queue[chemtrails.queue_index])):
self.reset() self.reset()
if not self.hidden: # if not self.hidden:
ds = self.get_display_surface() # ds = self.get_display_surface()
aa_filled_polygon(ds, self.get_points(), self.color) # aa_filled_polygon(ds, self.get_points(), self.color)
def get_points(self): def get_points(self):
if self.get_game().title.active: if self.get_game().title.active:
@ -1234,6 +1378,12 @@ class Light(Animation):
) )
def in_orientation(self, orientation): 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: if self.position == NS.LNW:
return orientation in (NS.N, NS.NW, NS.W) return orientation in (NS.N, NS.NW, NS.W)
elif self.position == NS.LNE: elif self.position == NS.LNE:
@ -1243,6 +1393,14 @@ class Light(Animation):
elif self.position == NS.LSW: elif self.position == NS.LSW:
return orientation in (NS.S, NS.NE, NS.W) 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): class Chemtrails(Sprite):

1
config
View File

@ -70,3 +70,4 @@ nw_color = #00FF88
ne_color = #FF88FF ne_color = #FF88FF
se_color = #2222FF se_color = #2222FF
sw_color = #FF2222 sw_color = #FF2222
center = 319, 376

BIN
resource/pad/pad_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B