import pyxel import random import math import string import util class State: def __init__(self, **kwargs): pass def update(self, app): pass def draw(self, app): pass VALID_GUESSES = { pyxel.KEY_A: "A", pyxel.KEY_B: "B", pyxel.KEY_C: "C", pyxel.KEY_D: "D", pyxel.KEY_E: "E", pyxel.KEY_F: "F", pyxel.KEY_G: "G", pyxel.KEY_H: "H", pyxel.KEY_I: "I", pyxel.KEY_J: "J", pyxel.KEY_K: "K", pyxel.KEY_L: "L", pyxel.KEY_M: "M", pyxel.KEY_N: "N", pyxel.KEY_O: "O", pyxel.KEY_P: "P", pyxel.KEY_Q: "Q", pyxel.KEY_R: "R", pyxel.KEY_S: "S", pyxel.KEY_T: "T", pyxel.KEY_U: "U", pyxel.KEY_V: "V", pyxel.KEY_W: "W", pyxel.KEY_X: "X", pyxel.KEY_Y: "Y", pyxel.KEY_Z: "Z", } class TitleScreen(State): def __init__(self, **kwargs): self.next_state_counter = 0 self.sent_input = False pyxel.stop() pyxel.playm(0, loop=True) def update(self, app): if self.next_state_counter != 0: self.next_state_counter += 1 if self.next_state_counter == 30: app.next_round() if not self.sent_input and util.if_continue_button(): self.next_state_counter += 1 pyxel.play(1, 0) self.sent_input = True def draw(self, app): pyxel.rect(0, 0, 64, 64, 11) pyxel.blt(0, 0, 0, 0, 32, 64, 64, 11) center_x = 32 center_y = 75 radius = 40 time_adjust = 20 colors = [5, 8, 10, 2] for i in range(19): portion = (360.0 / 18) / 3 t0 = app.frame - self.next_state_counter + portion * i t1 = t0 / time_adjust t2 = (t0 + portion) / time_adjust x1 = radius*math.cos(t1) + center_x y1 = radius*math.sin(t1) + center_y x2 = radius*math.cos(t2) + center_x y2 = radius*math.sin(t2) + center_y pyxel.tri(center_x, center_y, x1, y1, x2, y2, colors[i % len(colors)]) pyxel.text(8, 45, "Press Start!", 7 if int(app.frame/8)%2 else 0) class GuessingState(State): def __init__(self, **kwargs): self.guessed = False self.guess = kwargs.get("next_guess") self.guessed_at = kwargs["app"].frame self.via_next = kwargs.get("via_next") if self.guess: kwargs["app"].guessed.add(self.guess) self.guessed = True correct = False for word in kwargs["app"].phrase: for i, letter in enumerate(word): if letter == self.guess: correct = True if correct: pyxel.play(1, 0) else: pyxel.play(1, 1) def update(self, app): if not self.guessed: for key, value in VALID_GUESSES.items(): if pyxel.btnp(key) and value not in app.guessed: self.guessed = True self.guess = value app.guessed.add(value) self.guessed_at = app.frame correct = False for word in app.phrase: for i, letter in enumerate(word): if letter == self.guess: correct = True if correct: pyxel.play(1, 0) else: pyxel.play(1, 1) # TODO sum up outcomes for score? else: if app.frame - self.guessed_at > 50: # If next round missing_letter = False correct = False for word in app.phrase: for i, letter in enumerate(word): if letter not in app.guessed: missing_letter = True if not missing_letter: app.next_round(winner="THEM" if self.via_next else "YOU") elif self.via_next: app.set_state(SpinOrb) else: # chance for enemy to guess right increases each round if random.random() < (0.1 * (6 + app.round)): for word in app.phrase: for letter in word: if letter not in app.guessed: them_guess = letter break else: them_guess = random.choice(list( set(list(string.ascii_uppercase)) - app.guessed )) app.set_state(Dialog, dialog=[{ "who": "them", "text": f"I'd like to guess '{them_guess}'" }], next=GuessingState, next_kwargs={"next_guess": them_guess} ) def draw(self, app): pyxel.rect(0, 0, 64, 64, 3) pyxel.rect(3, 41, 58, 21, 1) for i in range(16): color = 10 if ((int(app.frame/10) + i) % 4) == 0 else 13 pyxel.rect(2 + 4*i, 2, 1, 1, color) color = 10 if ((int(app.frame/10) + 16- i) % 4) == 0 else 13 pyxel.rect(2 + 4*i, 33, 1, 1, color) y_offset = 4 for word in app.phrase: x_offset = (64-len(word)*8)/2 for i, letter in enumerate(word): index = 26 # default blank if letter in app.guessed: index = ord(letter) - ord('A') if app.round == 3: y_index = app.glyph_map[letter] elif app.round == 4: y_index = 1 else: y_index = 0 pyxel.blt(x_offset + i*8, y_offset, 0, index*8, y_index*8, 8, 8) if self.guessed and letter == self.guess and app.frame - self.guessed_at < 20: pyxel.rect(x_offset + i*8, y_offset, 8, 8, 10) y_offset += 10 pyxel.text(6, 35, app.category, 7) pyxel.text(6, 42, "GUESSED:", 7) pyxel.line(4, 48, 58, 48, 7) for i, value in enumerate(VALID_GUESSES.values()): pyxel.text(6+4*(i%13), 50 + 6*int(i/13), value, 7 if value in app.guessed else 13) class SpinOrb(State): def __init__(self, **kwargs): self.wheel_accel = 50 self.wheel_outcome = None self.wheel_spun = False self.wheel_time = 0 self.colors = [5, 8, 10, 2, 9] random.shuffle(self.colors) self.outcome_time = 50 def update(self, app): if not self.wheel_spun: if util.if_continue_button(): self.wheel_spun = True else: self.wheel_time += self.wheel_accel/30 if int(self.wheel_time/5) % 3 == 0: pyxel.play(1, 12) if self.wheel_time >= 80 and self.wheel_accel > 0: self.wheel_accel -= 0.3 elif self.wheel_accel < 1 and not self.wheel_outcome: self.wheel_accel = 0 self.wheel_outcome = random.choice(app.outcomes) # TODO random event, change state based on that self.outcome_time = 50 else: self.outcome_time -= 1 if self.outcome_time <= 0: if self.wheel_outcome == "GRAND PRIZE": # TODO grand prize ending pass app.set_state(GuessingState) def draw(self, app): pyxel.rect(0, 0, 64, 64, 11) pyxel.blt(0, 0, 0, 0, 64, 64, 64, 11) center_x = 32 center_y = 75 radius = 64 time_adjust = 20 for i in range(19): portion = (360.0 / 18) / 3 t0 = self.wheel_time + portion * i + 4 t1 = t0 / time_adjust t2 = (t0 + portion) / time_adjust x1 = radius*math.cos(t1) + center_x y1 = radius*math.sin(t1) + center_y x2 = radius*math.cos(t2) + center_x y2 = radius*math.sin(t2) + center_y pyxel.tri( center_x, center_y, x1, y1, x2, y2, self.colors[i % len(self.colors)]) pyxel.tri(30, 10, 32, 10, 30+int(self.wheel_time/5) % 3, 14, 0) if self.wheel_outcome: pyxel.text(20, 20, self.wheel_outcome, 7 if int(app.frame/8)%2 else 0) class Dialog(State): def __init__(self, **kwargs): self.dialog = kwargs["dialog"] self.dialog_index = 0 self.text_index = -1 self.next = kwargs.get("next") self.next_kwargs = kwargs.get("next_kwargs", {}) self.is_end = kwargs.get("is_end") if self.is_end: self.text_index = -20 def update(self, app): if self.dialog_index < len(self.dialog): # advance 1 char at a time if self.text_index < len(self.dialog[self.dialog_index]["text"]): if app.frame % 3 == 0: self.text_index += 1 if self.dialog[self.dialog_index]["who"] == "you": pyxel.play(1, 0) pyxel.play(1, random.choice([17, 18])) elif self.dialog[self.dialog_index]["who"] == "them": pyxel.play(1, random.choice([15, 16])) elif self.dialog[self.dialog_index]["who"] == "camera": pyxel.play(1, random.choice([13, 14])) if util.if_continue_button(): # skip to end of text if self.text_index < len(self.dialog[self.dialog_index]["text"]): self.text_index = len(self.dialog[self.dialog_index]["text"]) else: # move onto next text self.text_index = -1 self.dialog_index += 1 else: if self.next: self.next_kwargs["via_next"] = True app.set_state(self.next, **self.next_kwargs) else: app.set_state(SpinOrb) def draw(self, app): pyxel.rect(0, 0, 64, 64, 3) if not self.is_end: pyxel.bltm(0, 0, 0, 0, 0, 64, 64) else: pyxel.bltm(0, 0, 0, 64, 0, 64, 64) pod_blink = int(app.frame / 20) % 3 if pod_blink: pyxel.blt(40, 40, 0, 0, 176, 16, 16, 5) pyxel.blt(8, 40, 0, 0, 176, 16, 16, 5) # draw camera blink = int(app.frame / 20) % 2 y_offset = 96 if blink else 104 direction = 1 if int(app.frame/100) % 2 else -1 pyxel.blt(24, 8, 0, 48, y_offset, 16 * direction, 8, 5) # which form of "them" to use them_index = 0 if app.round > 2 else 3 them_dir = 1 if app.round > 2 else -1 pyxel.blt(8, 24, 0, 16 + 16 * them_index, 96, 16*them_dir, 16, 5) pyxel.blt(40, 24, 0, 32, 96 + 16, 16, 16, 5) if self.text_index > 0 and self.dialog_index < len(self.dialog): pyxel.rect(1, 41, 62, 21, 7) pyxel.rectb(1, 41, 62, 21, 0) if self.dialog[self.dialog_index]["who"] == "you": pyxel.tri(44, 34, 42, 41, 38, 41, 7) pyxel.trib(44, 34, 42, 41, 38, 41, 0) pyxel.rect(39, 41, 3, 1, 7) if self.text_index < len(self.dialog[self.dialog_index]["text"]): pyxel.blt(40, 24, 0, 32, 96 + 16 * (int(app.frame/5) % 5), 16, 16, 5) elif self.dialog[self.dialog_index]["who"] == "them": pyxel.tri(20, 36, 21, 41, 26, 41, 7) pyxel.trib(20, 36, 21, 41, 26, 41, 0) pyxel.rect(22, 41, 3, 1, 7) if self.text_index < len(self.dialog[self.dialog_index]["text"]): pyxel.blt(8, 24, 0, 16 + 16 * them_index, 96 + 16 * (int(app.frame/5) % 5), 16 * them_dir, 16, 5) elif self.dialog[self.dialog_index]["who"] == "camera": pyxel.tri(32, 20, 30, 41, 34, 41, 7) pyxel.trib(32, 20, 30, 41, 34, 41, 0) pyxel.rect(31, 41, 3, 1, 7) current_text = self.dialog[self.dialog_index]["text"][:self.text_index] line_start = 0 last_space = 0 lines = [] for i, c in enumerate(current_text): if c == ' ': last_space = i if i - line_start >= 15: lines.append(current_text[line_start:last_space].strip()) line_start = last_space last_space = i lines.append(current_text[line_start:].strip()) for i, line in enumerate(lines): pyxel.text(3, 43 + i*6, line, 0) if self.is_end: for i in range(random.randrange(int((self.dialog_index**3)/3)+1)): y = random.randrange(64) for j in range(64): pyxel.rect(j, y, 1, 1, random.choice([1, 7, 13])) class Credits(State): def __init__(self, **kwargs): pyxel.playm(0, loop=True) def update(self, app): if app.frame - app.state_change_at > 2000: pyxel.quit() def draw(self, app): if app.frame - app.state_change_at < 200: for y in range(64): for j in range(64): pyxel.rect(j, y, 1, 1, random.choice([1, 7, 13])) else: pyxel.rect(0, 0, 64, 64, 11) center_x = 32 center_y = 32 radius = 50 time_adjust = 20 colors = [5, 8, 10, 2] for i in range(19): portion = (360.0 / 18) / 3 t0 = app.frame + portion * i t1 = t0 / time_adjust t2 = (t0 + portion) / time_adjust x1 = radius*math.cos(t1) + center_x y1 = radius*math.sin(t1) + center_y x2 = radius*math.cos(t2) + center_x y2 = radius*math.sin(t2) + center_y pyxel.tri(center_x, center_y, x1, y1, x2, y2, colors[i % len(colors)]) pyxel.rect(16, 3, 35, 16, 0) pyxel.text(20, 5, "ORB OF", 7) pyxel.text(18, 12, "FORTUITY", 7) pyxel.rect(8, 41, 48, 16, 0) pyxel.text(16, 43, "Made by", 7) pyxel.text(10, 51, "Mark Powers", 7)