Compare commits

...

26 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
f2b9ab4d26 Added spinner audio file & relevant TODO 2025-06-26 08:01:44 +01:00
b2cc7a996f Did the "LEGO Island fix" 2025-06-26 07:29:31 +01:00
b0c752f946 Added TODOs 2025-06-26 07:22:09 +01:00
71693518a8 Added text margins 2025-06-26 07:21:57 +01:00
a8814fdfe1 Added basic spin animation 2025-06-26 07:21:41 +01:00
2 changed files with 151 additions and 24 deletions

BIN
assets/spinner-tick.mp3 Normal file

Binary file not shown.

175
game.py
View File

@ -1,21 +1,82 @@
import math as maths import math as maths
import logging import logging
import pygame import random
import time
import gameUtils import gameUtils
import NoPELib import NoPELib
import numpy as np import numpy as np
import pygame
# TODO: Prevent another animation from starting when the wheel's spinning
# 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)
def translate(value, aMin, aMax, bMin, bMax):
"""
Translates `value` to go from `aMin`-`aMax` to `bMin`-`bMax`
Stolen from StackOverflow because we don't have time to do shit
properly here.
"""
aRange = aMax - aMin
bRange = bMax - 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:
""" """
Rotates a surface clockwise in radians. Rotates a surface clockwise in radians and re-centres it.
""" """
# Calculate the degrees # Calculate the degrees
deg = radians*(180/maths.pi) deg = radians*(180/maths.pi)
# Rotate the surface re-centre
return rotateDeg(surface, deg)
def rotateDeg(surface: pygame.Surface, degrees: float) -> pygame.Surface:
"""
Rotates a surface clockwise in degrees and re-centres it.
"""
# Convert to CCW becaus for some reason that's what Pygame uses? # Convert to CCW becaus for some reason that's what Pygame uses?
deg *= -1 degrees *= -1
# Rotate the surface # Rotate surface which will expand it
return pygame.transform.rotate(surface, deg) rotSurface = pygame.transform.rotate(surface, degrees)
# re-centre it to the orinal surface bounding box
rotSurface = gameUtils.centre(rotSurface, surface.size)
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):
@ -31,44 +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
# Holds the decided speed of the wheel
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
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)
self.drawWheel()
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(), 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(gameUtils.centre(textSurf, self.wheelSurf.size), (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):
""" """
@ -77,14 +173,45 @@ class Game(gameUtils.Game):
Args: Args:
event (pygame.Event): The event that was fired. event (pygame.Event): The event that was fired.
""" """
pass if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
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.blit(gameUtils.centre(self.wheelSurf, self.size), (0, 0)) self.surf.fill((0, 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
if animTime < WCfg.spinFadeInTimes[0] + WCfg.spinFadeOutTimes[0]:
if animTime < WCfg.spinFadeInTimes[0]:
speed = translate(animTime, 0, WCfg.spinFadeInTimes[0], 0, self.wheelSpinSpeed)
else:
speed = translate(animTime, WCfg.spinFadeInTimes[0], WCfg.spinFadeInTimes[0]+WCfg.spinFadeOutTimes[0], self.wheelSpinSpeed, 0)
self.wheelRotation += speed * self.timeDelta
self.wheelRotation %= 360
# 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):
""" """