summaryrefslogtreecommitdiff
path: root/states.py
diff options
context:
space:
mode:
authorMark Powers <mark@marks.kitchen>2024-07-14 15:49:48 -0500
committerMark Powers <mark@marks.kitchen>2024-07-14 15:49:48 -0500
commitd4c45c02890143fb8cfbc60be9d2b79240d9ad8e (patch)
treeb7fc54bbf17742609ed1bd84dbffb0f6f1064822 /states.py
Initial commitmain
Diffstat (limited to 'states.py')
-rw-r--r--states.py382
1 files changed, 382 insertions, 0 deletions
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)
+