-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPieces.py
367 lines (289 loc) · 12.6 KB
/
Pieces.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# Pieces.py
from OpenGL.GL import *
import numpy as np
import math
from Cube import *
from Texture import Texture
import Camera
#[r,g,b,a]
colors = (
[0,1,1,1], # Cyan 0
[0,0,1,1], # Blue 1
[1,0.5,0,1], # Orange 2
[1,1,0,1], # Yellow 3
[0,1,0,1], # Green 4
[1,0,1,1], # Purple 5
[1,0,0,1] # Red 6
)
filepaths = (
"Textures/blueberryI.png",
"Textures/blackberriesJ.png",
"Textures/orangeL.png",
"Textures/bananaO.png",
"Textures/pearZ.png",
"Textures/grapesT.png",
"Textures/strawberryS.png"
)
borders = (
(-4, 4),
(-5, 30),
(-4, 4)
)
pieceNames = ["I", "J", "L", "O", "S", "T", "Z"] # List of pieces by name
cubeCount = 4 # Amount of cubes per piece
tetrisPieces = []
CubeList = []
# A function to create Tetris pieces
def createTetrisPieces():
for i in range(len(pieceNames)):
piece = Piece(position=(0, 18, 0), color=colors[i], name=pieceNames[i], filepath=filepaths[i])
tetrisPieces.append(piece)
return tetrisPieces
def checkCubeCol(piece):
# Collect position of all cubes in the piece
for cube in piece.cubes:
locPos = np.round(cube.GetCubePos()) + np.round(piece.GetPos())
# Check if any cube in CubeList is within a distance of 2 in all three axes
if any(all(np.abs(locPos - np.round(pos.GetCubePos())) < 2) for pos in CubeList):
#print("Overlaps")
return False # overlaps
return True # no overlaps
def freezeCubes(cube):
CubeList.append(cube)
# function for ending the game once the player has lost
def CheckForCeil():
if CubeList:
for cube in CubeList:
if np.rint(cube.GetCubePos()[1]) >= 6:
return False
return True
#Alfredo
#checks if bottom row is filled
def CheckForPoint():
count = 0 # counter for cubes at bottom
for cube in CubeList:
if cube.GetCubePos()[1] <= -4.9: # bottom y coord is -5
count += 1
if count >= 16: # if there are this many cubes at bottom
count = 0
return False
return True
#Alfredo
class Piece:
def __init__(self, position, color, name, filepath):
#Smooth rotations
#Tracks which direction the piece is currently rotating (left, right, down)
self.rotateDir = None
#Tracks how many degrees have been rotated in current rotation
self.radRotated = math.radians(0)
#Relative rotations
#The axis to rotate the pieces on (horizontal by default, changes when rotation is toggled)
self.rotateAxis = (0, -2, 0)
self.name = name
if self.name == "I":
self.localPositions = [(0, 0, 0), (0, -2, 0), (0, 2, 0), (0, 4, 0)]
elif self.name == "J":
self.localPositions = [(0, 0, 0), (0, -2, 0), (0, 2, 0), (-2, 2, 0)]
elif self.name == "L":
self.localPositions = [(0, 0, 0), (0, -2, 0), (-2, -2, 0), (0, 2, 0)]
elif self.name == "O":
self.localPositions = [(0, 0, 0), (-2, -2, 0), (0, -2, 0), (-2, 0, 0)]
elif self.name == "S":
self.localPositions = [(0, 0, 0), (0, -2, 0), (-2, -2, 0), (2, 0, 0)]
elif self.name == "T":
self.localPositions = [(0, 0, 0), (0, -2, 0), (-2, 0, 0), (2, 0, 0)]
elif self.name == "Z":
self.localPositions = [(0, 0, 0), (0, -2, 0), (2, -2, 0), (-2, 0, 0)]
self.filepath = filepath
self.cubes = [Cube(localPos, color, self.filepath) for localPos in self.localPositions]
self.position = position #takes position of each piece
self.lastPosition = self.position #sets last position
self.ang = 0
self.axis = (3,1,1)
self.color = np.asfarray(color) #takes a different color for each piece
self.transforms = [np.eye(4) for _ in range(cubeCount)]
#Set cubes within piece to be fully transparent on init
for cube in self.cubes:
cube.color[3] = 0
#Toggle cubes to appear
self.ToggleCubes(True, False)
def GetPos(self):
return self.position
def SetPos(self, position):
self.position = position
# Reset Cube Orientation for in gameplay
def ResetCubePos(self):
for i, cube in enumerate(self.cubes):
#reset each localPos
cube.SetCubePos(self.localPositions[i])
# Revert Cube Positions for rotation function
def RevertCubePos(self, lastPos):
for i, cube in enumerate(self.cubes):
#revert each localPos
cube.SetCubePos(lastPos[i])
#Change the value of appearing and disappearing for all cubes within piece (pass in a boolean for appear and disappear)
def ToggleCubes(self, appear, disappear):
for cube in self.cubes:
cube.appearing = appear
cube.disappearing = disappear
#Smooth Rotations
#Takes a direction (left, right, down) and toggles the piece to rotate that way over 1/6s
def ToggleRotate(self, dir):
#If the piece is not currently rotating, toggle it to rotate in the given direction
if self.rotateDir == None:
self.rotateDir = dir
#Reset rads rotated counter (for new rotation)
self.radRotated = 0
#Check axis facing the camera (for relative piece rotations)
curSide = Camera.getCurSide()
#If rotating left or right, rotate around y axis
if self.rotateDir == "left":
self.rotateAxis = (0, -2, 0)
elif self.rotateDir == "right":
self.rotateAxis = (0, 2, 0)
#If rotating down, rotate around x or z axis
elif self.rotateDir == "down":
if curSide == 'z':
self.rotateAxis = (-2, 0, 0)
elif curSide == '-z':
self.rotateAxis = (2, 0, 0)
elif curSide == 'x':
self.rotateAxis = (0, 0, -2)
elif curSide == '-x':
self.rotateAxis = (0, 0, 2)
def RotateSmooth(self, deltaTime):
# defines cur/prev pos in case to revert
curLocalPositions = [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)]
for i, cube in enumerate(self.cubes):
curPos = cube.GetCubePos()
curLocalPositions[i] = np.rint(curPos)
#Tracks the rotation direction for this frame (for edge case)
#This is needed in case rotation gets cancelled on the last frame of rotation (and self.rotateDir is None when reverting rotation)
rotateDirThisFrame = self.rotateDir
#Tracks if the rotation has failed
failed = False
center = np.asfarray(self.cubes[0].localPos)
#The magnitude of the angle of rotation (in radians)
mag = math.radians(90 * 6 * deltaTime)
if self.rotateDir == "left":
#If the rotation is almost complete, rotate the rest of the way
if (self.radRotated + mag) >= math.radians(90):
mag = math.radians(90) - self.radRotated
#Reset rotation counter and rotation status
#self.radRotated = math.radians(0)
self.rotateDir = None
else:
#self.radRotated += mag
pass
self.radRotated += mag
angle = mag
axis = self.rotateAxis
elif self.rotateDir == "right":
#If the rotation is almost complete, rotate the rest of the way
if (self.radRotated + mag) >= math.radians(90):
mag = math.radians(90) - self.radRotated
#Reset rotation counter and rotation status
#self.radRotated = math.radians(0)
self.rotateDir = None
else:
#self.radRotated += mag
pass
self.radRotated += mag
angle = mag
axis = self.rotateAxis
elif self.rotateDir == "down":
#If the rotation is almost complete, rotate the rest of the way
if (self.radRotated + mag) >= math.radians(90):
mag = math.radians(90) - self.radRotated
#Reset rotation counter and rotation status
#self.radRotated = math.radians(0)
self.rotateDir = None
else:
#self.radRotated += mag
pass
self.radRotated += mag
angle = -mag
axis = self.rotateAxis
rotation_matrix = axis_rotation_matrix(angle, axis)
#Perform the rotation
for cube in self.cubes:
cube.localPos = np.dot(cube.localPos - center, rotation_matrix) + center
# checks if in bounds or colliding and if so reverts cube positions
if not self.CheckInBounds() or not checkCubeCol(self):
#print("collision detected, reverting rotation")
failed = True
if failed:
#print("collision detected, reverting rotation")
#self.RevertCubePos(curLocalPositions)
#Revert piece back to its original orientation by reversing the current rotation
if rotateDirThisFrame == "down":
self.Rotate(self.radRotated, self.rotateAxis)
else:
#print("reverting horiz rotation")
self.Rotate(self.radRotated, (self.rotateAxis[0], -self.rotateAxis[1], self.rotateAxis[2]))
#Toggle the piece to stop rotating
self.rotateDir = None
#Smooth Rotations
# Rotates a piece in one frame
def Rotate(self, angle, axis):
curLocalPositions = [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)]
for i, cube in enumerate(self.cubes):
curPos = cube.GetCubePos()
curLocalPositions[i] = curPos
# Convert angle to radians
angle = math.radians(angle)
center = np.asfarray(self.cubes[0].localPos)
rotation_matrix = axis_rotation_matrix(angle, axis)
# checks if cubes are in bounds before completing rotation
for cube in self.cubes:
cube.localPos = np.rint(np.dot(cube.localPos - center, rotation_matrix)) + center
if not self.CheckInBounds() or not checkCubeCol(self):
self.RevertCubePos(curLocalPositions)
break
# Check if all cubes of a piece are in bounds
def CheckInBounds(self):
for cube in self.cubes:
for i in range(3): # for each axis (x,y,z)
pos = cube.GetCubePos() + np.rint(self.position)
# checks if each axis value is between border limits for that axis
if pos[i] <= borders[i][0] or pos[i] >= borders[i][1]:
return False # Cube is out of bounds for at least one dimension
return True # All cubes are within bounds for all dimensions
#NOTE: Fading in/out can no longer be paused (because this update is no longer locked behind pause status)
def Update(self, deltaTime, move, paused): #Takes pause status to gatekeep all update actions except ongoing fade and rotate
#If the game is paused, don't update position
if not paused:
self.ang += 50.0 * deltaTime
self.position += move
if not self.CheckInBounds():
self.position[0] = self.lastPosition[0]
self.position[2] = self.lastPosition[2]
# Save last position
self.lastPosition = np.copy(self.position)
#TEST: Update Cubes within piece regardless of pause status
for cube in self.cubes:
cube.Update(deltaTime)
#Smooth Rotations
if self.rotateDir != None:
self.RotateSmooth(deltaTime)
def Render(self):
#m = glGetDouble(GL_MODELVIEW_MATRIX)
#center = self.cubes[0].localPos
glPushMatrix()
glTranslatef(*self.position)
#glRotatef(self.ang, *center)
for cube in self.cubes:
cube.Render()
glPopMatrix()
#glLoadMatrixf(m)
# quaternians rotation matrix
def axis_rotation_matrix(angle, axis):
axis = np.asarray(axis)
axis = axis / np.sqrt(np.dot(axis, axis))
q = np.array([np.cos(angle / 2.0), *(-axis * np.sin(angle / 2.0))])
rotation_matrix = np.array([
[1 - 2 * (q[2]**2 + q[3]**2), 2 * (q[1] * q[2] - q[0] * q[3]), 2 * (q[1] * q[3] + q[0] * q[2])],
[2 * (q[1] * q[2] + q[0] * q[3]), 1 - 2 * (q[1]**2 + q[3]**2), 2 * (q[2] * q[3] - q[0] * q[1])],
[2 * (q[1] * q[3] - q[0] * q[2]), 2 * (q[2] * q[3] + q[0] * q[1]), 1 - 2 * (q[1]**2 + q[2]**2)],
])
return rotation_matrix