merged master in

This commit is contained in:
Frank DeMarco 2020-10-28 15:42:49 -04:00
commit 2f172566b0
17 changed files with 203 additions and 88 deletions

245
NS.py
View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
from random import randint, choice
from random import randint, choice, random
from math import pi
from copy import copy
from glob import iglob
@ -9,16 +9,17 @@ from threading import Thread
from serial import Serial, SerialException
from serial.tools import list_ports
from time import sleep
from PIL import Image
from pygame import Surface, Color, mixer
from pygame.event import clear
from pygame.mixer import Sound
from pygame.image import load, fromstring
from pygame.transform import rotate, flip
from pygame.transform import rotate, flip, scale
from pygame.time import get_ticks
from pygame.font import Font
from pygame.draw import aalines, lines
from pygame.gfxdraw import aapolygon, arc, polygon
from pygame.gfxdraw import aapolygon, arc, polygon, aaellipse, ellipse, filled_ellipse, filled_circle
from pygame.locals import *
from lib.pgfw.pgfw.Game import Game
@ -74,6 +75,7 @@ class NS(Game, Animation):
self.dialogue = Dialogue(self)
self.chemtrails = Chemtrails(self)
self.boss = Boss(self)
self.tony = Tony(self)
if self.serial_enabled():
self.serial_kill = False
self.serial_data = 0
@ -158,6 +160,7 @@ class NS(Game, Animation):
self.platform.reset()
self.dialogue.reset()
self.no_reset_elapsed = 0
self.title.activate()
def set_most_recent_time(self, score):
self.most_recent_time = score
@ -210,13 +213,12 @@ class NS(Game, Animation):
print("auto arduino reset triggered")
self.reset_arduino()
self.no_reset_elapsed = 0
self.title.update()
self.introduction.update()
self.ending.update()
self.boss.update()
if not self.introduction.active:
self.platform.update()
self.platform.update()
self.chemtrails.update()
self.title.update()
self.boss.update_dialogue()
self.wipe.update()
self.idle_elapsed += self.time_filter.get_last_frame_duration()
@ -314,8 +316,67 @@ class Meter(GameChild):
icon.update()
class Tony(Sprite):
def __init__(self, parent):
Sprite.__init__(self, parent)
dsr = self.get_display_surface().get_rect()
for offset in range(12):
w, h = dsr.w + 40, int(dsr.h * .65)
glow = Surface((w, h), SRCALPHA)
for ii, y in enumerate(range(h, 0, -8)):
hue = range(200, 140, -5)[(ii - offset) % 12]
alpha = min(100, int(round(y / float(h - 10) * 100)))
color = get_hsla_color(hue, 100, 50, alpha)
if ii == 0:
aaellipse(glow, w // 2, y, w // 2 - 4, h // 20, color)
ellipse(glow, w // 2, y, w // 2 - 4, h // 20, color)
filled_ellipse(glow, w // 2, y, w // 2 - 4, h // 20, color)
frame = load(self.get_resource("Big_Tony.png")).convert_alpha()
frame.blit(glow, (-20, int(dsr.h * .35)), None, BLEND_RGBA_SUB)
self.add_frame(frame)
class Video(Sprite):
def __init__(self, parent):
Sprite.__init__(self, parent, 100)
pattern = join(self.get_resource("gif"), "Boarding_*.gif")
self.gifs = []
for path in iglob(pattern):
self.gifs.append(Image.open(path))
print(self.gifs[-1].info)
self.gif = self.gifs[1]
self.mask = Surface((320, 320), SRCALPHA)
rect = self.mask.get_rect()
filled_circle(self.mask, rect.centerx, rect.centery, rect.centerx, (255, 255, 255))
def update(self):
if random() < .01:
while True:
selection = choice(self.gifs)
if selection != self.gif:
self.gif = selection
break
if random() < .005:
self.toggle_hidden()
self.gif.seek((self.gif.tell() + 1) % self.gif.n_frames)
frame = scale(
fromstring(self.gif.convert("RGBA").tobytes(), self.gif.size, "RGBA"),
(self.mask.get_width(), int(self.gif.width * self.gif.height / self.mask.get_width())))
copy = self.mask.copy()
rect = frame.get_rect()
rect.bottom = copy.get_rect().bottom
copy.blit(frame, rect, None, BLEND_RGBA_MIN)
self.clear_frames()
self.add_frame(copy)
Sprite.update(self)
class Title(GameChild):
UNLOCK_MOVES = NS.N, NS.NW, NS.E, NS.S
def __init__(self, parent):
GameChild.__init__(self, parent)
self.plank = Sprite(self)
@ -323,39 +384,43 @@ class Title(GameChild):
ds = self.get_display_surface()
dsr = ds.get_rect()
self.plank.location.center = dsr.center
self.slime_bag = Sprite(self)
self.slime_bag.load_from_path(self.get_resource("Title_slime_bag.png"), True)
self.slime_bag.location.bottomleft = dsr.bottomleft
image = load(self.get_resource("Title_border.png")).convert()
image.set_colorkey((0, 0, 0))
self.border = RainbowSprite(self, image, 30)
self.border.location.center = dsr.centerx, dsr.bottom - 100
self.text = Sprite(self)
self.text.load_from_path(self.get_resource("Title_text.png"), True, False, (255, 0, 0))
self.text.location.center = dsr.centerx, dsr.bottom - 100
self.angle = choice((pi / 4, 3 * pi / 4, 5 * pi / 4, 7 * pi / 4))
self.button_sound = self.get_audio().sfx["button"]
self.buttons = Button(self, NS.N, 10, 4), Button(self, NS.NW, 10, 4)
self.buttons[0].location.center = 277, 381
self.buttons[1].location.center = 453, 381
# self.angle = choice((pi / 4, 3 * pi / 4, 5 * pi / 4, 7 * pi / 4))
self.angle = 7 * pi / 4
self.background = Sprite(self)
self.background.load_from_path(self.get_resource("Title_tile.png"), True)
for y in range(0, dsr.h + self.background.location.h, self.background.location.h):
for x in range(0, dsr.w + self.background.location.w, self.background.location.w):
if x != 0 or y != 0:
self.background.add_location((x, y))
self.effect = Sprite(self, 100)
palette = (255, 255, 255), (255, 255, 128), (255, 255, 0)
thickness = 8
for offset in range(len(palette)):
frame = Surface(dsr.size)
for x in range(0, dsr.w, thickness):
frame.fill(palette[(offset + x) % len(palette)], (x, 0, thickness, dsr.h))
self.effect.add_frame(frame)
self.video = Video(self)
def reset(self):
self.activate()
self.first_pressed = False
self.first_pressed_elapsed = 0
for button in self.buttons:
button.unhide()
self.unlock_index = 0
def activate(self):
self.active = True
platform = self.get_game().platform
platform.activate()
platform.set_glowing(platform.get_buttons_from_edges([self.UNLOCK_MOVES[self.unlock_index]]))
self.get_game().chemtrails.activate()
def deactivate(self):
self.active = False
def activate_introduction(self):
def start_game(self):
self.deactivate()
self.get_game().introduction.activate()
self.get_game().set_most_recent_time(None)
self.get_game().boss.start_level(0)
def draw_scores(self):
step = 75
@ -372,7 +437,7 @@ class Title(GameChild):
text = entry
message = render_box(font, text, True, Color(255, 255, 255),
Color(128, 128, 128), Color(0, 0, 0), padding=2)
message.set_alpha(200)
message.set_alpha(230)
rect = message.get_rect()
rect.top = y
if ii < 5:
@ -392,47 +457,59 @@ class Title(GameChild):
return "%i:%02i.%i" % (minutes, seconds, fraction / 100)
def update(self):
'''
Move title, check button presses, and draw screen
'''
if self.active:
ds = self.get_display_surface()
ds.fill((255, 255, 255))
dsr = ds.get_rect()
if self.plank.location.right > dsr.right or self.plank.location.left < dsr.left:
self.angle = reflect_angle(self.angle, 0)
if self.plank.location.right > dsr.right:
self.plank.move(dsr.right - self.plank.location.right)
else:
self.plank.move(dsr.left - self.plank.location.left)
if self.plank.location.bottom > dsr.bottom or self.plank.location.top < dsr.top:
self.angle = reflect_angle(self.angle, pi)
if self.plank.location.bottom > dsr.bottom:
self.plank.move(dy=dsr.bottom - self.plank.location.bottom)
else:
self.plank.move(dy=dsr.top - self.plank.location.top)
dx, dy = get_delta(self.angle, 2, False)
self.plank.move(dx, dy)
self.plank.update()
self.slime_bag.update()
wipe = self.get_game().wipe
if not self.first_pressed and self.get_game().platform.get_edge_pressed() == NS.N:
self.effect.update()
# tiled background
self.background.move(-2, 2)
if self.background.location.right < 0:
self.background.move(self.background.location.w)
if self.background.location.top > 0:
self.background.move(dy=-self.background.location.h)
self.background.update(flags=BLEND_RGBA_MIN)
self.get_game().tony.update()
# advance unlock pattern
platform = self.get_game().platform
if not self.get_game().wipe.is_playing() and platform.get_edge_pressed() == self.UNLOCK_MOVES[self.unlock_index]:
self.first_pressed = True
self.first_pressed_elapsed = 0
self.buttons[0].hide()
self.button_sound.play()
elif not wipe.is_playing() and self.first_pressed and \
self.get_game().platform.get_edge_pressed() == NS.NW:
wipe.start(self.activate_introduction)
self.get_audio().play_sfx("confirm")
elif self.first_pressed:
if self.unlock_index == len(self.UNLOCK_MOVES) - 1:
platform.set_glowing([])
self.get_game().wipe.start(self.start_game)
self.get_audio().play_sfx("confirm")
else:
self.unlock_index += 1
platform.set_glowing(platform.get_buttons_from_edges([self.UNLOCK_MOVES[self.unlock_index]]))
self.get_audio().play_sfx("land_0")
# reset unlock pattern if idle
if self.first_pressed:
self.first_pressed_elapsed += self.get_game().time_filter.get_last_frame_duration()
# if self.first_pressed_elapsed > 4000:
# self.first_pressed = False
# self.first_pressed_elapsed = 0
# self.buttons[0].unhide()
self.border.update()
self.text.update()
if self.first_pressed_elapsed > 1000 * 60 * 1:
self.reset()
platform.update()
self.get_game().chemtrails.update()
# bounce the gif around the screen
if self.video.location.right > dsr.right or self.video.location.left < dsr.left:
self.angle = reflect_angle(self.angle, 0)
if self.video.location.right > dsr.right:
self.video.move(dsr.right - self.video.location.right)
else:
self.video.move(dsr.left - self.video.location.left)
if self.video.location.bottom > dsr.bottom or self.video.location.top < dsr.top:
self.angle = reflect_angle(self.angle, pi)
if self.video.location.bottom > dsr.bottom:
self.video.move(dy=dsr.bottom - self.video.location.bottom)
else:
self.video.move(dy=dsr.top - self.video.location.top)
dx, dy = get_delta(self.angle, 5, False)
self.video.move(dx, dy)
if not platform.get_pressed():
self.video.update()
self.draw_scores()
for button in self.buttons:
button.update()
class Dialogue(Animation):
@ -477,7 +554,7 @@ class Dialogue(Animation):
def stop_speech(self):
if self.speech_channel is not None:
self.speech_channel.stop()
self.speech_channel = None
self.speech_channel = None
def deactivate(self):
self.stop_speech()
@ -494,7 +571,7 @@ class Dialogue(Animation):
def set_name(self, text):
font = Font(self.get_resource(self.FONT_PATH), self.FONT_SIZE)
self.name = Sprite(self)
self.name.add_frame(font.render(text, True, self.TEXT_COLOR))
self.name.add_frame(font.render(text, True, self.TEXT_COLOR).convert_alpha())
self.name.location.midleft = self.name_box.location.left + 5, self.name_box.location.centery
def show_text(self, text):
@ -526,11 +603,11 @@ class Dialogue(Animation):
lines = self.full_text[:self.text_index].split("\n")
frame = Surface((self.text_box.location.w - 10, 30 * len(lines)), SRCALPHA)
for ii, line in enumerate(lines):
surface = font.render(line, True, self.TEXT_COLOR)
surface = font.render(line, True, self.TEXT_COLOR).convert_alpha()
frame.blit(surface, (0, 30 * ii))
message.add_frame(frame)
message.location.topleft = self.text_box.location.left + 9, self.text_box.location.top + 8
message.update()
message.add_frame(frame)
message.location.topleft = self.text_box.location.left + 9, self.text_box.location.top + 8
message.update()
class Introduction(Animation):
@ -554,9 +631,8 @@ class Introduction(Animation):
self.words = []
for word in "hey you lizard slime bag show me you can scrape".split(" "):
font = Font(self.get_resource(Dialogue.FONT_PATH), 96)
sprite = RainbowSprite(self, font.render(word, True, (255, 0, 0)), 30)
sprite = RainbowSprite(self, font.render(word, True, (255, 0, 0)).convert_alpha(), 30)
self.words.append(sprite)
self.tony = load(self.get_resource("Big_Tony.png")).convert()
self.skateboard = Sprite(self)
self.skateboard.load_from_path(self.get_resource("Introduction_skateboard.png"), True)
self.slime_bag = Sprite(self)
@ -694,7 +770,7 @@ class Introduction(Animation):
else:
platform.set_glowing(platform.get_buttons_from_edges(
[self.TUTORIAL_MOVES[self.tutorial_index]]))
self.get_display_surface().blit(self.tony, (0, 0))
self.get_game().tony.update()
self.slime_bag.update()
self.skateboard.update()
for word in self.words:
@ -730,7 +806,7 @@ class SkipPrompt(GameChild):
left += self.buttons[-1].location.width + AdvancePrompt.BUTTON_SPACING
self.text = Sprite(self)
font = Font(self.get_resource(Dialogue.FONT_PATH), 18)
self.text.add_frame(font.render("TO SKIP", True, (0, 0, 0)))
self.text.add_frame(font.render("TO SKIP", True, (0, 0, 0)).convert_alpha())
self.text.location.midleft = (
self.buttons[2].location.right + 5,
self.buttons[2].location.centery)
@ -1029,7 +1105,7 @@ class Platform(GameChild):
class Light(Animation):
MAX_GLOW_INDEX = 25
INTRODUCTION_OFFSET = 80
TITLE_OFFSET = 0
def __init__(self, parent, color, position):
Animation.__init__(self, parent)
@ -1077,7 +1153,7 @@ class Light(Animation):
def update(self):
Animation.update(self)
if not self.get_game().introduction.active:
if not self.get_game().title.active:
boss = self.get_game().boss
chemtrails = self.get_game().chemtrails
if boss.queue and boss.brandish_complete and not self.is_playing(self.glow) \
@ -1091,10 +1167,10 @@ class Light(Animation):
aa_filled_polygon(ds, self.get_points(), self.color)
def get_points(self):
if self.get_game().introduction.active:
if self.get_game().title.active:
points = []
for point in self.points:
points.append((point[0], point[1] - self.INTRODUCTION_OFFSET))
points.append((point[0], point[1] - self.TITLE_OFFSET))
return points
else:
return self.points
@ -1168,17 +1244,17 @@ class Chemtrails(Sprite):
if self.active:
self.orient()
Sprite.update(self)
if not self.get_game().introduction.active:
if not self.get_game().title.active:
boss = self.get_game().boss
if boss.queue:
self.timer.tick()
self.attack()
if self.timer.amount < 0:
self.life.decrease()
if not boss.is_playing(boss.show_end_dialogue):
if not boss.is_playing(boss.show_end_dialogue, include_delay=True):
self.timer.reset()
boss.combo()
if not boss.is_playing(boss.show_introduction_dialogue):
if not boss.is_playing(boss.show_introduction_dialogue, include_delay=True):
self.timer.update()
self.life.update()
# self.boys.update()
@ -1199,7 +1275,7 @@ class Chemtrails(Sprite):
boss.sword.block()
if self.queue_index == len(queue):
self.timer.reset()
if not boss.is_playing(boss.show_end_dialogue):
if not boss.is_playing(boss.show_end_dialogue, include_delay=True):
boss.combo()
self.get_audio().play_sfx("complete_pattern_3")
else:
@ -1209,7 +1285,7 @@ class Chemtrails(Sprite):
def orient(self):
ds = self.get_display_surface()
edge = self.get_game().platform.get_edge_pressed()
dy = -Light.INTRODUCTION_OFFSET if self.get_game().introduction.active else 0
dy = -Light.TITLE_OFFSET if self.get_game().title.active else 0
if edge is not None:
self.set_frameset(edge + 1)
self.unhide()
@ -1681,17 +1757,17 @@ class Countdown(GameChild):
dsr = self.get_display_surface().get_rect()
font = Font(self.get_resource(Dialogue.FONT_PATH), 76)
self.heading = Sprite(self)
self.heading.add_frame(font.render("CONTINUE?", True, (0, 0, 0), (255, 255, 255)))
self.heading.add_frame(font.render("CONTINUE?", True, (0, 0, 0), (255, 255, 255)).convert_alpha())
self.heading.location.midtop = dsr.centerx, 50
self.game_over = Sprite(self)
self.game_over.add_frame(font.render("GAME OVER", True, (0, 0, 0), (255, 255, 255)))
self.game_over.add_frame(font.render("GAME OVER", True, (0, 0, 0), (255, 255, 255)).convert_alpha())
self.game_over.location.center = dsr.centerx, dsr.centery - 40
self.glyphs = []
for ii in range(10):
glyph = Sprite(self)
frame = Surface((140, 140))
frame.fill((255, 255, 255))
digits = font.render("%i" % ii, True, (0, 0, 0), (255, 255, 255))
digits = font.render("%i" % ii, True, (0, 0, 0), (255, 255, 255)).convert_alpha()
rect = digits.get_rect()
rect.center = frame.get_rect().center
frame.blit(digits, rect)
@ -1872,7 +1948,6 @@ class Ending(Animation):
def __init__(self, parent):
Animation.__init__(self, parent)
self.tony = load(self.get_resource("Big_Tony.png")).convert()
self.slime_bag = Sprite(self)
self.slime_bag.load_from_path(self.get_resource("Introduction_slime_bag.png"), True)
self.slime_bag.location.center = self.get_display_surface().get_rect().centerx, 300
@ -1934,7 +2009,7 @@ class Ending(Animation):
else:
self.start_wipe()
self.advance_prompt.cancel_first_press()
self.get_display_surface().blit(self.tony, (0, 0))
self.get_game().tony.update()
self.slime_bag.update()
dsr = self.get_display_surface().get_rect()
if self.text.location.right > dsr.right or self.text.location.left < dsr.left:

6
config
View File

@ -1,6 +1,6 @@
[setup]
license = Public Domain
title = Electric Scrapeboard
title = Scrapeboard
url = http://shampoo.ooo/games/esb
version = 0.2.3
init-script = OPEN-GAME
@ -8,7 +8,7 @@ additional-packages = lib
data-exclude = local/, *.pyc
[display]
caption = Electric Scrapeboard
caption = Scrapeboard
show-framerate = no
dimensions = 640, 480
fullscreen = no
@ -26,7 +26,7 @@ sfx-volume = .8
[input]
buffer = 0
arduino-port = /dev/ttyACM0
serial = no
serial = yes
[time]
timer-max-time = 10000

38
record_test.py Normal file
View File

@ -0,0 +1,38 @@
import pyaudio
import wave
CHUNK = 1024
FORMAT = pyaudio.paInt32
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
print("* recording")
frames = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data)
print("* done recording")
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

BIN
resource/Big_Tony.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 22 KiB

BIN
resource/Title_tile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
resource/gif/Boarding_0.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 MiB

BIN
resource/gif/Boarding_1.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 MiB

BIN
resource/gif/Boarding_2.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

BIN
resource/gif/Boarding_3.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 MiB

BIN
resource/gif/Boarding_4.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

View File

@ -17,3 +17,5 @@
312561
173960
237842
171298
164711

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 MiB

After

Width:  |  Height:  |  Size: 30 B

1
www/Boarding_0.gif Symbolic link
View File

@ -0,0 +1 @@
../resource/gif/Boarding_0.gif

Before

Width:  |  Height:  |  Size: 5.3 MiB

After

Width:  |  Height:  |  Size: 30 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 MiB

After

Width:  |  Height:  |  Size: 30 B

1
www/Boarding_1.gif Symbolic link
View File

@ -0,0 +1 @@
../resource/gif/Boarding_1.gif

Before

Width:  |  Height:  |  Size: 8.1 MiB

After

Width:  |  Height:  |  Size: 30 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 MiB

After

Width:  |  Height:  |  Size: 30 B

1
www/Boarding_2.gif Symbolic link
View File

@ -0,0 +1 @@
../resource/gif/Boarding_2.gif

Before

Width:  |  Height:  |  Size: 5.0 MiB

After

Width:  |  Height:  |  Size: 30 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 MiB

After

Width:  |  Height:  |  Size: 30 B

1
www/Boarding_3.gif Symbolic link
View File

@ -0,0 +1 @@
../resource/gif/Boarding_3.gif

Before

Width:  |  Height:  |  Size: 6.4 MiB

After

Width:  |  Height:  |  Size: 30 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 MiB

After

Width:  |  Height:  |  Size: 30 B

View File

@ -0,0 +1 @@
../resource/gif/Boarding_4.gif

Before

Width:  |  Height:  |  Size: 5.2 MiB

After

Width:  |  Height:  |  Size: 30 B