Skip to content

Commit d1a062c

Browse files
authored
Merge pull request ufosc#136 from RyderKeeny/main
Adding DDR Minigame
2 parents 9b9bcce + 47b0083 commit d1a062c

File tree

1 file changed

+238
-0
lines changed

1 file changed

+238
-0
lines changed
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
init python:
2+
from renpy.display.image import Image
3+
from renpy.display.motion import Transform
4+
import time
5+
import random
6+
7+
# Global game variables
8+
target_time = 3.8 # Base target time for block interaction
9+
block_creation_count = 0
10+
max_block_creation = 10 # Adjusted per difficulty
11+
gameScore = 0 # Track player score
12+
13+
# RhythmBlock class definition
14+
class RhythmBlock:
15+
def __init__(self, image_path, x, y, speed, transform_name, zoom):
16+
self.sprite = Image(image_path)
17+
self.x = x
18+
self.y = y
19+
self.speed = speed
20+
self.transform_name = transform_name
21+
self.zoom = zoom
22+
self.hit = False
23+
self.start_time = time.time()
24+
25+
@staticmethod
26+
def difficultyRating(level):
27+
global target_time, max_block_creation, block_speed, blockrepition
28+
29+
# Base values for the Easy difficulty
30+
base_target_time = 3.8
31+
base_max_blocks = 10
32+
base_speed = 5
33+
base_rep_time = 2
34+
35+
if level == "Easy":
36+
target_time = base_target_time
37+
max_block_creation = base_max_blocks
38+
block_speed = base_speed
39+
blockrepition = base_rep_time
40+
41+
elif level == "Medium":
42+
target_time = base_target_time * 0.8 # 20% faster
43+
max_block_creation = int(base_max_blocks * 1.2)
44+
block_speed = base_speed * 1.2
45+
blockrepition = base_rep_time * 0.6
46+
47+
elif level == "Hard":
48+
target_time = base_target_time * 0.6 # 40% faster
49+
max_block_creation = int(base_max_blocks * 1.5)
50+
block_speed = base_speed * 1.5
51+
blockrepition = base_rep_time * 0.3
52+
53+
else:
54+
raise ValueError("Invalid difficulty level. Choose 'Easy', 'Medium', or 'Hard'.")
55+
56+
def get_elapsed_time(self):
57+
"""Calculate the elapsed time since block creation."""
58+
return time.time() - self.start_time
59+
60+
def check_hit_timing(self, target_time):
61+
"""Check if the block is hit within an acceptable time window."""
62+
time_difference = abs(self.get_elapsed_time() - target_time)
63+
64+
if self.hit:
65+
return "already_hit"
66+
if time_difference <= 0.3:
67+
self.hit = True
68+
return "perfect"
69+
elif time_difference <= 0.6:
70+
self.hit = True
71+
return "good"
72+
elif time_difference <= 0.9:
73+
self.hit = True
74+
return "bad"
75+
else:
76+
return "missed"
77+
78+
# Subclasses for different block types
79+
class BurgerBlock(RhythmBlock):
80+
def __init__(self, x, y, speed):
81+
super().__init__("DDR_images/burger.png", x, y, speed, move_arrow_1, 0.15)
82+
83+
class FriesBlock(RhythmBlock):
84+
def __init__(self, x, y, speed):
85+
super().__init__("DDR_images/Fries.png", x, y, speed, move_arrow_2, 0.15)
86+
87+
class SodaBlock(RhythmBlock):
88+
def __init__(self, x, y, speed):
89+
super().__init__("DDR_images/Soda.png", x, y, speed, move_arrow_3, 0.15)
90+
91+
class TendiesBlock(RhythmBlock):
92+
def __init__(self, x, y, speed):
93+
super().__init__("DDR_images/tendies.png", x, y, speed, move_arrow_4, 0.12)
94+
95+
# List to hold active blocks
96+
active_blocks = []
97+
block_classes = [BurgerBlock, FriesBlock, SodaBlock, TendiesBlock]
98+
99+
def random_block_order():
100+
"""Generate and append a random block if creation limit has not been reached."""
101+
global block_creation_count, max_block_creation, block_speed
102+
103+
if block_creation_count < max_block_creation:
104+
chosen_block = random.choice(block_classes)(1100, -50, block_speed)
105+
active_blocks.append(chosen_block)
106+
block_creation_count += 1
107+
108+
def log_key_press(block_name, elapsed_time):
109+
"""Log key press times for debugging purposes."""
110+
print(f"[DEBUG] Key pressed for {block_name} at {elapsed_time:.2f} seconds after block creation.")
111+
112+
def handle_block_hit(block_type, target_time): # will need to be modifyed so that it removes the renpy.notify and creates image popups of perfect, good, bad, etc.
113+
"""Register block hits and update the score."""
114+
global gameScore
115+
matching_blocks = [block for block in active_blocks if isinstance(block, block_type) and not block.hit]
116+
if matching_blocks:
117+
block = matching_blocks[-1]
118+
elapsed_time = block.get_elapsed_time()
119+
log_key_press(block.__class__.__name__, elapsed_time)
120+
result = block.check_hit_timing(target_time)
121+
122+
if result == "perfect":
123+
renpy.notify("Perfect!")
124+
gameScore += 10
125+
elif result == "good":
126+
renpy.notify("Good!")
127+
gameScore += 5
128+
elif result == "bad":
129+
renpy.notify("Bad!")
130+
gameScore += 1
131+
elif result == "missed":
132+
renpy.notify("Missed!")
133+
else:
134+
renpy.notify("Already hit!")
135+
136+
else:
137+
renpy.notify(f"No {block_type.__name__} blocks to hit!")
138+
139+
140+
141+
def sprite_manager(image, xpos, ypos, zoom=1.0):
142+
"""Return a Transform with given image and attributes."""
143+
return Transform(image, xpos=xpos, ypos=ypos, zoom=zoom)
144+
145+
# Transform definitions
146+
transform move_arrow_1:
147+
xalign 0.425
148+
linear config.screen_height / 800.0 * target_time yalign 1.5
149+
repeat
150+
151+
transform move_arrow_2:
152+
xalign 0.515
153+
linear config.screen_height / 800.0 * target_time yalign 1.5
154+
repeat
155+
156+
transform move_arrow_3:
157+
xalign 0.6
158+
linear config.screen_height / 800.0 * target_time yalign 1.5
159+
repeat
160+
161+
transform move_arrow_4:
162+
xalign 0.68
163+
linear config.screen_height / 800.0 * target_time yalign 1.5
164+
repeat
165+
166+
167+
168+
# Screens for the game
169+
screen instructions_DDR:
170+
frame:
171+
xsize 800
172+
ysize 700
173+
align (0.5, 0.5)
174+
background "#333333"
175+
padding (20, 20)
176+
vbox:
177+
null height 10
178+
spacing 20
179+
text "Welcome to the DDR-style Rhythm Game!" color "#FFFFFF" size 40
180+
text "Instructions:" color "#FFFFFF" size 30
181+
for instruction in [
182+
"1. Watch as blocks fall from the top of the screen.",
183+
"2. Press the corresponding arrow keys when the blocks reach the target zone.",
184+
"3. Hit the keys with perfect timing to score the most points!",
185+
"4. Try to score as many points as you can."
186+
]:
187+
text instruction color "#FFFFFF" size 25
188+
null height 20
189+
text "Choose your difficulty:" color "#FFFFFF" size 30 align (0.5, 0.5)
190+
191+
null height 20
192+
hbox:
193+
spacing 100
194+
align((0.5, 0.5))
195+
textbutton "Easy" action [Function(RhythmBlock.difficultyRating, "Easy"), Hide("instructions_DDR"), Show("DDRgame")]
196+
textbutton "Medium" action [Function(RhythmBlock.difficultyRating, "Medium"), Hide("instructions_DDR"), Show("DDRgame")]
197+
textbutton "Hard" action [Function(RhythmBlock.difficultyRating, "Hard"), Hide("instructions_DDR"), Show("DDRgame")]
198+
199+
screen DDRgame:
200+
frame:
201+
xsize 1920
202+
ysize 1000
203+
background "map_images/Food_bytes#2.jpg"
204+
add sprite_manager("DDR_images/scaleBackground.png", 90, -5)
205+
for xpos in [1100, 950, 800, 650, 500]:
206+
add sprite_manager("DDR_images/straight-line.png", xpos, -50)
207+
208+
for block in active_blocks:
209+
add block.sprite at block.transform_name zoom block.zoom
210+
211+
text "Score: [gameScore]" xpos 20 ypos 20 color "#FFFFFF" size 40
212+
213+
for xpos in [760, 915, 1065, 1215]:
214+
add sprite_manager("DDR_images/zone.png", xpos, 900, zoom=0.10)
215+
216+
key "K_LEFT" action Function(handle_block_hit, BurgerBlock, target_time)
217+
key "K_UP" action Function(handle_block_hit, FriesBlock, target_time)
218+
key "K_DOWN" action Function(handle_block_hit, SodaBlock, target_time)
219+
key "K_RIGHT" action Function(handle_block_hit, TendiesBlock, target_time)
220+
221+
if block_creation_count >= max_block_creation:
222+
timer 6.0 action Show("score_results", transition=dissolve, layer="overlay")
223+
224+
timer blockrepition action Function(random_block_order) repeat True
225+
226+
screen score_results:
227+
frame:
228+
xsize 1000
229+
ysize 1000
230+
align (0.5, 0.5)
231+
background "#333333"
232+
padding (20, 20)
233+
vbox:
234+
spacing 20
235+
align (0.5, 0.5)
236+
text "Final Score: [gameScore]" color "#FFFFFF" size 40 align (0.5, 0.5)
237+
text "Thank you for playing!" color "#FFFFFF" size 30 align (0.5, 0.5)
238+
textbutton "Leave" action [Hide('score_results'), Show('overviewMap')] align (0.5, 0.5)

0 commit comments

Comments
 (0)