Compare commits

...

21 Commits

Author SHA1 Message Date
36d646a26f Slightly cleaned up arraw draw 2025-06-27 07:22:37 +01:00
32c33ae613 Added TODOs 2025-06-27 07:18:50 +01:00
97bd53e491 Moved wheel config to its own class 2025-06-26 18:19:45 +01:00
0dc6555499 Changed text margin 2025-06-26 16:55:15 +01:00
8b47026f0e Added arrow 2025-06-26 16:55:06 +01:00
097dd7486a Fixed bug in text rendering 2025-06-26 16:54:58 +01:00
6adb31f466 Updated debug line code 2025-06-26 16:54:49 +01:00
db4f7d05f7 Added wheel margin 2025-06-26 16:54:33 +01:00
dafeb80b77 Fixed typo 2025-06-26 16:44:40 +01:00
3e4907f192 Moved wheel drawing into its own function 2025-06-26 16:44:27 +01:00
1366c10fd7 Added item shuffling 2025-06-26 16:43:40 +01:00
a12a583444 Linted 2 lines 2025-06-26 14:53:57 +01:00
dce162ba4a Made the first item appear on the right 2025-06-26 14:53:46 +01:00
c8b25ff53b Fixed big with which way the text rendered 2025-06-26 14:52:56 +01:00
94fa33ebd0 Made KEYDOWN event more flexible 2025-06-26 14:51:06 +01:00
a1c5641eee Added global font & render current item 2025-06-26 14:50:41 +01:00
471de3ef6e Created WheelItem class 2025-06-26 14:48:33 +01:00
e8ee151267 Removed TODO 2025-06-26 14:47:11 +01:00
95e4df2810 Implemented spinner ticking sound 2025-06-26 12:32:11 +01:00
23d0ca1ee2 Changed how the spinner is rendered 2025-06-26 12:32:03 +01:00
de4749cac9 Removed debug line 2025-06-26 10:12:15 +01:00
2 changed files with 108 additions and 38 deletions

146
game.py
View File

@ -9,11 +9,9 @@ import NoPELib
import numpy as np import numpy as np
import pygame import pygame
# TODO: Add the ability to re-draw the wheel to hightlight the selected item
# TODO: Prevent another animation from starting when the wheel's spinning # TODO: Prevent another animation from starting when the wheel's spinning
# TODO: Convery fade in / out time to be random # TODO: Convert fade in / out time to be random
# TODO: Make random finer (to prevent a range of 4-5 only having two possible speeds) # TODO: Make random finer (to prevent a range of 4-5 only having two possible speeds)
# TODO: Utilise spinner.mp3 (ideally without using 2k audio channels)
def translate(value, aMin, aMax, bMin, bMax): def translate(value, aMin, aMax, bMin, bMax):
""" """
@ -22,8 +20,8 @@ def translate(value, aMin, aMax, bMin, bMax):
properly here. properly here.
""" """
aRange = (aMax - aMin) aRange = aMax - aMin
bRange = (bMax - bMin) bRange = bMax - bMin
return (((value - aMin) * bRange) / aRange) + bMin return (((value - aMin) * bRange) / aRange) + bMin
def rotateRad(surface: pygame.Surface, radians: float) -> pygame.Surface: def rotateRad(surface: pygame.Surface, radians: float) -> pygame.Surface:
@ -47,6 +45,39 @@ def rotateDeg(surface: pygame.Surface, degrees: float) -> pygame.Surface:
rotSurface = gameUtils.centre(rotSurface, surface.size) rotSurface = gameUtils.centre(rotSurface, surface.size)
return rotSurface return rotSurface
class WCfg:
"""
Holds all the Wheel Configurations.
"""
# The background colour of the wheel
bgColour = (25, 25, 25)
# The colour of the dividing lines
divColour = (75, 75, 75)
# The colour of the text
txtColour = (255, 255, 255)
# How much space is between the wheel and the screen
margin = 50
# The minimum and maximum time it takes (in seconds)
# for the wheel to get to full speed.
spinFadeInTimes = [0.25, 0.25]
# The minimum and maximum time it takes (in seconds)
# for the wheel to get to go back to being stationary.
spinFadeOutTimes = [3, 4]
# The minimum and maximum speeds of the wheel in deg/s
spinSpeedRange = [360, 1000]
# How many pixels away from the outer side of the
# circle the text should be
textMargin = 10
# The font to use for drawing the item text
font = pygame.font.SysFont('', 32)
class WheelItem:
# TODO: Add integration with NoPE-Lib
# TODO: Allow this to modify game attributes so it can hide the wheel and respin
def __init__(self, text):
self.text = text
class Game(gameUtils.Game): class Game(gameUtils.Game):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
""" """
@ -61,58 +92,79 @@ class Game(gameUtils.Game):
# Don't remove this. It does important things. :3 # Don't remove this. It does important things. :3
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.wheelSurf = pygame.Surface([min(self.size)]*2) # Don't ask. self.wheelSurf = pygame.Surface([min(self.size)-WCfg.margin*2]*2) # Don't ask.
self.wheelRotation = 0 self.wheelRotation = 0
# How long (in seconds) it takes for the wheel to get to full speed
self.WHEEL_SPIN_FADE_IN = 0.25
# How long (in seconds) it takes for the wheel to go back to stationary
self.WHEEL_SPIN_FADE_OUT = 2.5
# The minimum and maximum speeds of the wheel
self.WHEEL_SPEED_RANGE = [360, 550]
# Holds the decided speed of the wheel # Holds the decided speed of the wheel
self.wheelSpinSpeed = 0 self.wheelSpinSpeed = 0
# Holds the decided fade in time
self.animFadeIn = 0
# Holds the decided fade out time
self.animFadeOut = 0
# When the wheel started its spin animation # When the wheel started its spin animation
self.wheelAnimStart = 0 self.wheelAnimStart = 0
# The ticker sound effect
self.tickSfx = pygame.mixer.Sound("./assets/spinner-tick.mp3")
# The last time (in time.perf_counter()) the ticker was played
self.lastTickTime = 0
# The last index the ticker ticked on
self.lastTickItem = -1
font = pygame.font.SysFont('', 32) self.items = []
self.items.append(WheelItem('Drink 1 shot'))
self.items.append(WheelItem('Drink 2 shots'))
self.items.append(WheelItem('Get shocked at 50%'))
self.items.append(WheelItem('Get shocked at 100%'))
self.items.append(WheelItem('Get slapped by\neveryone in the room'))
self.items.append(WheelItem('Respin, wheel hidden'))
random.shuffle(self.items)
items = [f'Example item {i+1}' for i in range(17)] self.font = pygame.font.SysFont('', 50)
# How many pixels away from the outer side of the self.drawWheel()
# circle the text should be
textMargin = 5 def drawWheel(self):
# Calculate circle radius # Calculate circle radius
circleR = self.wheelSurf.size[0] / 2 circleR = self.wheelSurf.size[0] / 2
# Draw the base circle # Draw the base circle
pygame.draw.circle(self.wheelSurf, (25, 25, 25), (circleR, circleR), circleR) pygame.draw.circle(self.wheelSurf, WCfg.bgColour, (circleR, circleR), circleR)
# Calculate the gap, in radians, between each item # Calculate the gap, in radians, between each item
gap = maths.pi * 2 / len(items) gap = maths.pi * 2 / len(self.items)
for idx, item in enumerate(items): for idx, item in enumerate(self.items):
# Calculate the radians of this item # Calculate the radians of this item
rad = (idx / len(items)) * maths.pi * 2 rad = (idx / len(self.items)) * maths.pi * 2
# Calculate the vector from that # Calculate the vector offset by half the item gap
vector = maths.cos(rad), maths.sin(rad) # This is because the text should be in the middle,
# and the lines (which is what this is used by) on the edges
vector = maths.cos(rad+gap/2), maths.sin(rad+gap/2)
# Calculate the end position of a line based on the vector # Calculate the end position of a line based on the vector
endPos = np.add(np.multiply(vector, circleR), circleR) endPos = np.add(np.multiply(vector, circleR), circleR)
# Draw the dividing line # Draw the dividing line
pygame.draw.line(self.wheelSurf, (128, 128, 128), (circleR, circleR), endPos) pygame.draw.line(self.wheelSurf, WCfg.divColour, (circleR, circleR), endPos)
# Create a surface that our text will be rendered onto # Create a surface that our text will be rendered onto
textSurf = pygame.Surface(self.wheelSurf.size, pygame.SRCALPHA) textSurf = pygame.Surface(self.wheelSurf.size, pygame.SRCALPHA)
# Render the item text # Render the item text
text = font.render(item, True, (255, 255, 255)) text = WCfg.font.render(item.text, True, WCfg.txtColour)
# Draw the item text onto the text surface, centred to the right # Draw the item text onto the text surface, centred to the right
textSurf.blit(text, (textSurf.size[0]-text.get_width()-textMargin, self.size[1]//2 - text.get_height()//2)) textSurf.blit(text, (textSurf.size[0]-text.get_width()-WCfg.textMargin, textSurf.size[1]//2 - text.get_height()//2))
# Draw debug lines # Draw debug lines
#pygame.draw.rect(textSurf, (0, 128, 255), (0, 0, textSurf.size[0], textSurf.size[1]), 1) #if idx < 1:
#pygame.draw.line(textSurf, (255, 0, 0), (circleR, circleR), (textSurf.size[0], circleR)) # pygame.draw.rect(textSurf, (0, 128, 255), (0, 0, textSurf.size[0], textSurf.size[1]), 1)
#pygame.draw.line(textSurf, (0, 255, 0), (circleR, circleR), (circleR, textSurf.size[1])) # pygame.draw.line(textSurf, (255, 0, 0), (circleR, circleR), (textSurf.size[0], circleR))
# pygame.draw.line(textSurf, (0, 255, 0), (circleR, circleR), (circleR, textSurf.size[1]))
# Rotate the text surface # Rotate the text surface
textSurf = rotateRad(textSurf, rad+gap/2) textSurf = rotateRad(textSurf, -rad)
# Blit the text surface to the wheel surface # Blit the text surface to the wheel surface
self.wheelSurf.blit(textSurf, (0, 0)) self.wheelSurf.blit(textSurf, (0, 0))
def spinnerTick(self):
# If there's a tick already playing
if time.perf_counter() - self.lastTickTime < self.tickSfx.get_length():
return
self.tickSfx.play()
self.lastTickTime = time.perf_counter()
def onEvent(self, event): def onEvent(self, event):
""" """
@ -121,27 +173,45 @@ class Game(gameUtils.Game):
Args: Args:
event (pygame.Event): The event that was fired. event (pygame.Event): The event that was fired.
""" """
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: if event.type == pygame.KEYDOWN:
self.wheelSpinSpeed = random.randint(*self.WHEEL_SPEED_RANGE) if event.key == pygame.K_SPACE:
self.wheelAnimStart = time.perf_counter() self.wheelSpinSpeed = random.randint(*WCfg.spinSpeedRange)
self.wheelAnimStart = time.perf_counter()
def update(self): def update(self):
""" """
Ran once per frame, put your drawing code and any Ran once per frame, put your drawing code and any
game logic that should be ran once per frame in here. game logic that should be ran once per frame in here.
""" """
self.surf.fill((0, 0, 0))
self.surf.blit(rotateDeg(gameUtils.centre(self.wheelSurf, self.size), self.wheelRotation), (0, 0)) self.surf.blit(rotateDeg(gameUtils.centre(self.wheelSurf, self.size), self.wheelRotation), (0, 0))
arrowX = (self.size[0] - self.wheelSurf.size[0]) // 2 + self.wheelSurf.size[0]
arrowY = self.size[1] // 2
arrowSize = 20
arrowPollys = ((arrowX, arrowY), (arrowX+arrowSize, arrowY-arrowSize//2), (arrowX+arrowSize, arrowY+arrowSize//2))
pygame.draw.polygon(self.surf, (255, 255, 255), arrowPollys)
animTime = time.perf_counter() - self.wheelAnimStart animTime = time.perf_counter() - self.wheelAnimStart
if animTime < self.WHEEL_SPIN_FADE_IN + self.WHEEL_SPIN_FADE_OUT: if animTime < WCfg.spinFadeInTimes[0] + WCfg.spinFadeOutTimes[0]:
if animTime < self.WHEEL_SPIN_FADE_IN: if animTime < WCfg.spinFadeInTimes[0]:
speed = translate(animTime, 0, self.WHEEL_SPIN_FADE_IN, 0, self.wheelSpinSpeed) speed = translate(animTime, 0, WCfg.spinFadeInTimes[0], 0, self.wheelSpinSpeed)
else: else:
speed = translate(animTime, self.WHEEL_SPIN_FADE_IN, self.WHEEL_SPIN_FADE_IN+self.WHEEL_SPIN_FADE_OUT, self.wheelSpinSpeed, 0) speed = translate(animTime, WCfg.spinFadeInTimes[0], WCfg.spinFadeInTimes[0]+WCfg.spinFadeOutTimes[0], self.wheelSpinSpeed, 0)
self.wheelRotation += speed * self.timeDelta self.wheelRotation += speed * self.timeDelta
self.wheelRotation %= 360 self.wheelRotation %= 360
print(self.wheelRotation)
# Calculates the current item index with... I don't know, magic?
currentItemIdx = round((self.wheelRotation / 360) * len(self.items)) % len(self.items)
# If the last tick was on a different index,
if currentItemIdx != self.lastTickItem:
# Click again
self.spinnerTick()
# And set the last tick index to the current one
self.lastTickItem = currentItemIdx
self.surf.blit(self.font.render(self.items[currentItemIdx].text, True, (255, 255, 255)), (0, 0))
def close(self): def close(self):
""" """