1
- import os , arcade , arcade .gui , random , json , time
1
+ import os , arcade , arcade .gui , random , json , time , copy
2
2
3
3
from game .sprites import Shape
4
- from utils .constants import SHAPES , CELL_SIZE , ROWS , COLS , OUTLINE_WIDTH , COLORS , COMBO_TIME , button_style
4
+ from utils .constants import SHAPES , CELL_SIZE , ROWS , COLS , OUTLINE_WIDTH , COLORS , COMBO_MOVES , button_style
5
5
from utils .preload import button_texture , button_hovered_texture , click_sound , break_sound
6
6
class Game (arcade .gui .UIView ):
7
7
def __init__ (self , pypresence_client ):
@@ -11,21 +11,22 @@ def __init__(self, pypresence_client):
11
11
12
12
self .pypresence_client = pypresence_client
13
13
14
- self .occupied = {}
15
- self .shapes = []
14
+ self .tile_grid = {}
15
+ self .sprite_grid = {}
16
16
17
17
self .shape_to_place = random .choice (list (SHAPES .keys ()))
18
18
self .shape_color = random .choice (COLORS )
19
-
20
19
self .next_shape_to_place = random .choice (list (SHAPES .keys ()))
21
20
self .next_shape_color = random .choice (COLORS )
21
+ self .shape_data = SHAPES [self .shape_to_place ]
22
22
23
- self .start_x = self .window .width / 2 - (COLS * (CELL_SIZE + OUTLINE_WIDTH )) / 2 + ( CELL_SIZE / 2 )
23
+ self .start_x = self .window .width / 2 - (COLS * (CELL_SIZE + OUTLINE_WIDTH )) / 2
24
24
self .start_y = self .window .height - (ROWS * (CELL_SIZE + OUTLINE_WIDTH )) - (CELL_SIZE / 2 )
25
25
self .shape_center_x = 0
26
26
self .shape_center_y = 0
27
27
self .can_place_shape = True
28
- self .empty_grid = {}
28
+ self .is_game_over = False
29
+ self .combo_moves_left = COMBO_MOVES
29
30
30
31
if os .path .exists ("data.json" ):
31
32
with open ("data.json" , "r" ) as file :
@@ -34,7 +35,6 @@ def __init__(self, pypresence_client):
34
35
self .high_score = 0
35
36
36
37
self .tiles_to_destroy : list [tuple [int , int ]] = []
37
- self .tiles_to_destroy_classes : dict [tuple [int , int ], arcade .SpriteSolidColor ] = {}
38
38
39
39
self .score = 0
40
40
self .combo = 0
@@ -60,7 +60,7 @@ def on_show_view(self):
60
60
self .setup_grid ()
61
61
62
62
self .mouse_shape = Shape (0 , 0 , self .shape_to_place , self .shape_color , self .mouse_shape_list )
63
- self .next_shape_ui = Shape (self .window .width - (CELL_SIZE * 3 ), self .window .height - (CELL_SIZE * 3 ), self .next_shape_to_place , self .next_shape_color , self .shape_list )
63
+ self .next_shape_ui = Shape (self .window .width - (CELL_SIZE * 4 ), self .window .height - (CELL_SIZE * 4 ), self .next_shape_to_place , self .next_shape_color , self .shape_list )
64
64
65
65
self .score_box = self .anchor .add (arcade .gui .UIBoxLayout (space_between = 10 , vertical = False ), anchor_x = "center" , anchor_y = "top" )
66
66
@@ -92,11 +92,11 @@ def on_mouse_motion(self, x, y, dx, dy):
92
92
93
93
self .can_place_shape = True
94
94
tile_positions = []
95
- for offset_col , offset_row in SHAPES [ self .shape_to_place ] :
95
+ for offset_col , offset_row in self .shape_data :
96
96
tile_col = grid_col + offset_col
97
97
tile_row = grid_row + offset_row
98
98
99
- if not (0 <= tile_row < ROWS and 0 <= tile_col < COLS ) or self .occupied [tile_row ][tile_col ] or (tile_row , tile_col ) in self .tiles_to_destroy :
99
+ if not (0 <= tile_row < ROWS and 0 <= tile_col < COLS ) or self .tile_grid [tile_row ][tile_col ] or (tile_row , tile_col ) in self .tiles_to_destroy :
100
100
self .can_place_shape = False
101
101
break
102
102
@@ -112,23 +112,23 @@ def on_mouse_motion(self, x, y, dx, dy):
112
112
113
113
for row in range (ROWS ):
114
114
for col in range (COLS ):
115
- if self .empty_grid [row ][col ]:
116
- self .empty_grid [row ][col ].color = (* self .shape_color [:- 1 ], 170 ) if self .can_place_shape and (row , col ) in tile_positions else arcade .color .GRAY
115
+ if not self .tile_grid [row ][col ]:
116
+ self .sprite_grid [row ][col ].color = (* self .shape_color [:- 1 ], 170 ) if self .can_place_shape and (row , col ) in tile_positions else arcade .color .GRAY
117
117
else :
118
- self .occupied [row ][col ].color = (* self .shape_color [:- 1 ], 170 ) if (row , col ) in self .collided_tile_positions else self .occupied [row ][col ].original_color
118
+ self .sprite_grid [row ][col ].color = (* self .shape_color [:- 1 ], 170 ) if (row , col ) in self .collided_tile_positions else self .sprite_grid [row ][col ].original_color
119
119
120
120
self .mouse_shape .update (self .shape_to_place , self .shape_color , x , y )
121
121
122
122
def setup_grid (self ):
123
123
for row in range (ROWS ):
124
- self .occupied [row ] = {}
125
- self .empty_grid [row ] = {}
124
+ self .tile_grid [row ] = {}
125
+ self .sprite_grid [row ] = {}
126
126
127
127
for col in range (COLS ):
128
128
self .create_empty_tile (row , col )
129
129
130
130
def create_empty_tile (self , row , col ):
131
- self .occupied [row ][col ] = 0
131
+ self .tile_grid [row ][col ] = 0
132
132
133
133
center_x = self .start_x + col * (CELL_SIZE + OUTLINE_WIDTH )
134
134
center_y = self .start_y + row * (CELL_SIZE + OUTLINE_WIDTH )
@@ -141,12 +141,12 @@ def create_empty_tile(self, row, col):
141
141
)
142
142
self .shape_list .append (tile )
143
143
144
- self .empty_grid [row ][col ] = tile
144
+ self .sprite_grid [row ][col ] = tile
145
145
146
146
def check_collisions (self , grid_col , grid_row ):
147
- modified_grid = { row : { col : ( 1 if value else 0 ) for col , value in self . occupied [ row ]. items ()} for row in self .occupied }
147
+ modified_grid = copy . deepcopy ( self .tile_grid )
148
148
149
- for offset_col , offset_row in SHAPES [ self .shape_to_place ] :
149
+ for offset_col , offset_row in self .shape_data :
150
150
tile_col = grid_col + offset_col
151
151
tile_row = grid_row + offset_row
152
152
modified_grid [tile_row ][tile_col ] = 1
@@ -165,111 +165,112 @@ def check_collisions(self, grid_col, grid_row):
165
165
166
166
return collided_tiles
167
167
168
- def update_game (self ):
169
- for row , col in self .collided_tile_positions :
170
- self .tiles_to_destroy .append ((row , col ))
171
- self .tiles_to_destroy_classes [(row , col )] = self .occupied [row ][col ]
172
-
173
- self .score += 25 + (10 * self .combo )
174
-
175
- break_sound .play ()
176
-
177
- self .combo += 1
178
- self .last_combo = time .perf_counter ()
179
-
168
+ def on_update (self , _ ):
180
169
self .score_label .text = f"Score: { self .score } " + (f" Combo: X{ self .combo } " if self .combo else "" )
181
170
182
171
if self .score > self .high_score :
183
172
self .high_score = self .score
184
173
self .high_score_label .text = f"High Score: { self .high_score } "
185
174
186
- self .check_game_over ()
187
-
188
- def on_update (self , _ ):
189
- if time .perf_counter () - self .last_combo >= COMBO_TIME :
190
- self .combo = 0
191
- self .score_label .text = f"Score: { self .score } "
192
-
193
175
for row , col in self .tiles_to_destroy [:]:
194
- tile = self .tiles_to_destroy_classes . get (( row , col ))
176
+ tile = self .sprite_grid [ row ][ col ]
195
177
if not tile :
196
178
self .tiles_to_destroy .remove ((row , col ))
197
- self .tiles_to_destroy_classes .pop ((row , col ), None )
198
179
continue
199
180
200
181
tile .scale = (tile .scale_x - 0.05 , tile .scale_y - 0.05 )
201
182
202
183
if tile .scale_x <= 0.05 :
184
+ tile .color = arcade .color .GRAY
185
+ tile .original_color = arcade .color .GRAY
186
+ tile .scale = (1 , 1 )
187
+ self .tile_grid [row ][col ] = 0
203
188
self .tiles_to_destroy .remove ((row , col ))
204
- self .tiles_to_destroy_classes .pop ((row , col ))
205
- tile .remove_from_sprite_lists ()
206
- del tile
207
-
208
- self .create_empty_tile (row , col )
209
189
210
190
def check_game_over (self ):
211
191
for grid_row in range (ROWS ):
212
192
for grid_col in range (COLS ):
213
193
can_place = True
214
194
215
- for offset_col , offset_row in SHAPES [ self .shape_to_place ] :
195
+ for offset_col , offset_row in self .shape_data :
216
196
tile_col = grid_col + offset_col
217
197
tile_row = grid_row + offset_row
218
198
219
- if not (tile_row , tile_col ) in self . tiles_to_destroy and ( not ( 0 <= tile_row < ROWS and 0 <= tile_col < COLS ) or self . occupied [ tile_row ][ tile_col ] ):
199
+ if not (0 <= tile_row < ROWS and 0 <= tile_col < COLS ):
220
200
can_place = False
201
+ break
202
+
203
+ if (tile_row , tile_col ) in self .tiles_to_destroy :
204
+ continue
205
+
206
+ if self .tile_grid [tile_row ][tile_col ]:
207
+ can_place = False
208
+ break
221
209
222
210
if can_place :
223
211
return
224
212
225
- self .game_over_window = self .anchor .add (arcade .gui .UIBoxLayout (), anchor_x = "center" , anchor_y = "center" )
226
- self .game_over_window ._bg_color = arcade .color .BLACK
227
- self .game_over_label = self .game_over_window .add (arcade .gui .UILabel (text = "GAME OVER" , font_name = "Roboto" , font_size = 80 , text_color = arcade .color .WHITE ))
213
+ self .is_game_over = True
214
+
215
+ self .game_over_box = self .anchor .add (arcade .gui .UIBoxLayout (), anchor_x = "center" , anchor_y = "center" )
216
+ self .game_over_box ._bg_color = arcade .color .BLACK
217
+
218
+ self .game_over_label = self .game_over_box .add (arcade .gui .UILabel (text = "GAME OVER" , font_name = "Roboto" , font_size = 80 , text_color = arcade .color .WHITE ))
228
219
229
220
self .mouse_shape_list .clear ()
230
221
231
222
self .window .set_mouse_visible (True )
232
223
233
224
def on_key_press (self , symbol : int , modifiers : int ) -> bool | None :
234
- super ().on_key_press (symbol , modifiers )
235
225
if symbol == arcade .key .ESCAPE :
236
226
self .main_exit ()
237
227
238
228
def on_mouse_press (self , x : int , y : int , button : int , modifiers : int ) -> bool | None :
239
- super ().on_mouse_press (x , y , button , modifiers )
240
-
241
229
grid_col = int ((x - self .start_x + (CELL_SIZE / 2 )) // (CELL_SIZE + OUTLINE_WIDTH ))
242
230
grid_row = int ((y - self .start_y + (CELL_SIZE / 2 )) // (CELL_SIZE + OUTLINE_WIDTH ))
243
231
244
232
if self .can_place_shape :
245
233
if self .settings_dict .get ("sfx" , True ):
246
234
click_sound .play (volume = self .settings_dict .get ("sfx_volume" , 50 ) / 100 )
247
235
248
- shape = Shape (self .shape_center_x , self .shape_center_y , self .shape_to_place , self .shape_color , self .shape_list )
249
- self .shapes .append (shape )
250
-
251
- n = 0
252
-
253
- for offset_col , offset_row in SHAPES [self .shape_to_place ]:
236
+ for offset_col , offset_row in self .shape_data :
254
237
tile_col = grid_col + offset_col
255
238
tile_row = grid_row + offset_row
256
- self .occupied [tile_row ][tile_col ] = shape .tiles [n ]
257
- self .occupied [tile_row ][tile_col ].original_color = shape .shape_color
258
- self .shape_list .remove (self .empty_grid [tile_row ][tile_col ])
259
- self .empty_grid [tile_row ][tile_col ] = None
260
239
261
- n += 1
240
+ self .tile_grid [tile_row ][tile_col ] = 1
241
+ self .sprite_grid [tile_row ][tile_col ].color = self .shape_color
242
+ self .sprite_grid [tile_row ][tile_col ].original_color = self .shape_color
262
243
263
244
self .score += 5
264
245
246
+ for row , col in self .collided_tile_positions :
247
+ self .tiles_to_destroy .append ((row , col ))
248
+
249
+ self .score += 25 + (10 * self .combo )
250
+
251
+ break_sound .play ()
252
+
253
+ if self .collided_tile_positions :
254
+ self .combo += 1
255
+ self .combo_moves_left = COMBO_MOVES
256
+ self .last_combo = time .perf_counter ()
257
+ else :
258
+ self .combo_moves_left -= 1
259
+
260
+ if self .combo_moves_left == 0 :
261
+ self .combo = 0
262
+
265
263
self .shape_to_place = self .next_shape_to_place
266
264
self .shape_color = self .next_shape_color
265
+ self .shape_data = SHAPES [self .shape_to_place ]
267
266
268
- self .next_shape_to_place = random .choice (list (SHAPES .keys ()))
269
- self .next_shape_color = random .choice (COLORS )
270
- self .next_shape_ui .update (self .next_shape_to_place , self .next_shape_color )
267
+ if not self .is_game_over :
268
+ self .check_game_over ()
271
269
272
- self .update_game ()
270
+ if not self .is_game_over : # This check makes sure to not re-create the next shape if its game over. This confused me, cause the shape it showed could have been placed.
271
+ self .next_shape_to_place = random .choice (list (SHAPES .keys ()))
272
+ self .next_shape_color = random .choice (COLORS )
273
+ self .next_shape_ui .update (self .next_shape_to_place , self .next_shape_color )
273
274
274
275
def on_draw (self ):
275
276
self .window .clear ()
0 commit comments