From d4c45c02890143fb8cfbc60be9d2b79240d9ad8e Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Sun, 14 Jul 2024 15:49:48 -0500 Subject: Initial commit --- states.py | 382 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 states.py (limited to 'states.py') diff --git a/states.py b/states.py new file mode 100644 index 0000000..c055965 --- /dev/null +++ b/states.py @@ -0,0 +1,382 @@ +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) + -- cgit v1.2.3