-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.py
More file actions
1273 lines (1200 loc) · 71.7 KB
/
main.py
File metadata and controls
1273 lines (1200 loc) · 71.7 KB
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
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from pygame import *
import copy
import os
# Starting up pygame and necessary components
os.environ['SDL_VIDEO_CENTERED'] = '1' # Centering the screen
init() # Starting up pygame
size = width, height = 800, 600
screen = display.set_mode(size)
display.set_caption("Super Mario Bros!") # Setting the window title
display.set_icon(transform.scale(image.load("assets/sprites/mario/smallMarioJump.png"),(32,32))) # Setting the screen icon
# Declaring colours
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
SKYBLUE = (107, 140, 255)
# Declaring Variables
page = "menu" # Variable for the current game state
fpsCounter = time.Clock() # FPS counter
marioPos = [40, 496, 0, 0, "Right", 0] # X, Y, VX, VY, direction, state
# X, Y: Variables to keep track of mario's position on screen
# VX, VY: Variables to keep track of mario's X and Y velocity
# direction: Variable to keep track of the direction mario is facing
# state: 0 for small mario, 1 for big mario
marioStats = [True, 0, False, False, False, False, False, 0] # onGround, jumpFrames, inGround, isCrouch, onPlatform, isFalling, isAnimating, invulFrames
# onGround: Boolean to see if mario is on a solid ground
# jumpFrames: Variable to keep track of frames user has held space for
# inGround: Boolean to see if mario has fallen through the floor
# isCrouch: Boolean to see if mario is crouching
# onPlatform: Boolean to see if mario's last position was on a platform
# isFalling: Boolean to see if mario has stopped jumping and should fall
# isAnimating: Boolean to see if we need to pause the game and change mario's state
# invulFrames: Variable to keep track of the frames where mario is invulnerable
marioScore = [0, 0, 5] # Points, Coins, Lives
marioFrame = [0, 0, 0] # List to keep track of mario's sprites and his changing animation
marioAccelerate = 0.2 # The rate at which mario can speed up and slow down
backPos = 0 # Position of the background
levelNum = 0 # Variable to keep track of the current level
selected = 0 # Variable for current selected option in menu
# Loading Pictures
titleLogo = transform.scale(image.load("assets/sprites/title/logo.png"), (480,220))
titleSelect = transform.scale(image.load("assets/sprites/title/select.png"), (24,24))
mutePic = transform.scale(image.load("assets/sprites/title/muted.png"), (45,45))
backgroundPics = [transform.scale(image.load("assets/backgrounds/level_1.png").convert(), (9086, 600)),
transform.scale(image.load("assets/backgrounds/level_2.png").convert(), (10065, 600)),
transform.scale(image.load("assets/backgrounds/level_3.png").convert(), (9200, 600)),
transform.scale(image.load("assets/backgrounds/level_4.png").convert(), (16380, 600)),
transform.scale(image.load("assets/backgrounds/level_5.png").convert(), (10000, 600))]
winPics = [transform.scale(image.load("assets/backgrounds/win.png").convert(), (800, 600)),
transform.scale(image.load("assets/sprites/title/peach.png").convert(), (42, 69))]
marioSpriteNames = ["smallmariojump" , "bigmariojump" , "bigmariocrouch" , "smallmariodead" , "bigmariochange", "smallmariochange"]
marioSpriteNamesFlag = ["flagsmall1", "flagsmall2", "flagbig1", "flagbig2", "flagsmall2", "flagbig2"]
marioSprites = [[image.load("assets/sprites/mario/smallmario"+str(i)+".png").convert_alpha() for i in range(1,5)],
[image.load("assets/sprites/mario/bigmario"+str(i)+".png").convert_alpha() for i in range(1,5)],
[image.load("assets/sprites/mario/"+str(i)+".png").convert_alpha() for i in marioSpriteNames],
[image.load("assets/sprites/mario/"+str(i)+".png").convert_alpha() for i in marioSpriteNamesFlag]]
brickSprites=[[image.load("assets/sprites/bricks/question"+str(i)+".png").convert_alpha() for i in range(3,0,-1)],
[image.load("assets/sprites/bricks/brick.png").convert_alpha(),
image.load("assets/sprites/bricks/blockidle.png").convert_alpha()]]
brickPiece = transform.scale(image.load("assets/sprites/bricks/brickpiece.png").convert_alpha(), (21,21))
statCoin = [image.load("assets/sprites/title/coin"+str(i)+".png").convert_alpha() for i in range(3,0,-1)]
coinsPic = [[image.load("assets/sprites/coins/coinidle"+str(i)+".png").convert_alpha() for i in range(3,0,-1)],
[image.load("assets/sprites/coins/coinmove"+str(i)+".png").convert_alpha() for i in range(1,5)]]
itemsPic = [image.load("assets/sprites/items/mushroom.png").convert_alpha()]
enemiesPic = [[transform.scale(image.load("assets/sprites/enemies/goomba"+str(i)+".png").convert_alpha(), (42,42)) for i in range(1,4)],
[transform.scale(image.load("assets/sprites/enemies/bulletbill.png").convert_alpha(),(48,42)),
transform.scale(image.load("assets/sprites/enemies/bulletgun.png").convert_alpha(),(42,81)),
transform.scale(image.load("assets/sprites/enemies/bulletgunext.png").convert_alpha(),(42,45))],
[transform.scale(image.load("assets/sprites/enemies/spiny"+str(i)+".png").convert_alpha(),(42,42)) for i in range(1,3)]]
flagPic = [transform.scale(image.load("assets/sprites/items/flagpole.png").convert_alpha(), (42, 420)),
transform.scale(image.load("assets/sprites/items/flag.png").convert_alpha(), (42, 42))]
# Resizing, Flipping, and Reordering Pictures
statCoin = [transform.scale(pic, (15,24)) for pic in statCoin] # Resizing the coin icon sprites
statCoin = statCoin + statCoin[::-1] # Taking the coin icon sprites and adding a reversed version
for subList in range(len(marioSprites)): # Loop to go through Mario's sprite and resizing depending on if its small Mario's or big Mario's
for pic in range(len(marioSprites[subList])):
if marioSprites[subList][pic].get_height() == 16:
marioSprites[subList][pic] = transform.scale(marioSprites[subList][pic], (42, 42))
else:
marioSprites[subList][pic] = transform.scale(marioSprites[subList][pic], (42, 84))
marioSprites[3][4], marioSprites[3][5] = transform.flip(marioSprites[3][4], True, False), transform.flip(marioSprites[3][5], True, False)
for subList in range(len(brickSprites)): # Going through each brick sprite and resizing it
for pic in range(len(brickSprites[subList])):
brickSprites[subList][pic] = transform.scale(brickSprites[subList][pic], (42,42))
brickSprites[0] = brickSprites[0] + brickSprites[0][::-1] # Taking the brick sprites and adding a reversed version
brickPiece = [transform.flip(brickPiece, False, True), # Taking the brick debris and flipping the pictures
brickPiece,
transform.flip(brickPiece, True, True),
transform.flip(brickPiece, True, False)]
for subList in range(len(coinsPic)): # Going through each coin and resizing it
for pic in range(len(coinsPic[subList])):
coinsPic[subList][pic] = transform.scale(coinsPic[subList][pic], (30,36))
for pic in range(len(itemsPic)): # Going though each item and resizing it
itemsPic[pic] = transform.scale(itemsPic[pic], (42,42))
coinsPic[0] = coinsPic[0] + coinsPic[0][::-1] # Taking the coin pictures and adding a reversed version
# Declaring all fonts (with different sizes)
marioFontThin = font.Font("assets/fonts/marioFont.ttf", 12)
marioFont = font.Font("assets/fonts/marioFont.ttf", 18)
marioFontBig = font.Font("assets/fonts/marioFont.ttf", 22)
marioFontSuperBig = font.Font("assets/fonts/marioFont.ttf", 30)
# Creating text
playText = marioFont.render("play", False, WHITE)
instructText = marioFont.render("instructions", False, WHITE)
creditText = marioFont.render("credits", False, WHITE)
quitText = marioFont.render("quit", False, WHITE)
pauseText = marioFont.render("paused", False, WHITE)
helpText = marioFont.render("press esc to exit game", False, WHITE)
marioText = marioFontBig.render("mario", False, WHITE)
timeText = marioFontBig.render("time", False, WHITE)
worldText = marioFontBig.render("world", False, WHITE)
overText = marioFontBig.render("Game over", False, WHITE)
instructHelp = marioFontSuperBig.render("Instructions", False, WHITE)
moveRightHelp = marioFont.render("Move Right - D", False, WHITE)
moveLeftHelp = marioFont.render("Move Left - A", False, WHITE)
jumpHelp = marioFont.render("Jump - Space", False, WHITE)
crouchHelp = marioFont.render("Crouch/Fast Fall - S", False, WHITE)
pauseHelp = marioFont.render("Pause - P", False, WHITE)
musicPauseHelp = marioFont.render("Mute/Unmute Music - M", False, WHITE)
backTextHelp = marioFont.render("Back",False,WHITE)
creditTitleHelp = marioFontSuperBig.render("Game Created By: ",False,WHITE)
creditTextHelp1 = marioFont.render("Armaan Randhawa",False,WHITE)
creditTextHelp2 = marioFont.render("Kevin Cui",False,WHITE)
creditTextHelp3 = marioFont.render("Henry Zhang",False,WHITE)
winText1 = marioFont.render("Thank You Mario!",False,WHITE)
winText2 = marioFont.render("Your quest is now over.",False,WHITE)
winText3 = marioFont.render("We present you a new quest.",False,WHITE)
winText4 = marioFont.render("Press Enter to return to menu.",False,WHITE)
# Loading all sound files
pauseSound = mixer.Sound("assets/music/effects/pause.wav")
backgroundSound = mixer.Sound("assets/music/songs/mainSong.ogg")
backgroundFastSound = mixer.Sound("assets/music/songs/mainSongFast.ogg")
deathSound = mixer.Sound("assets/music/songs/death.wav")
flagSound = mixer.Sound("assets/music/songs/flag.wav")
doneSound = mixer.Sound("assets/music/songs/leveldone.wav")
doneworldSound = mixer.Sound("assets/music/songs/worlddone.wav")
gameDoneSound = mixer.Sound("assets/music/songs/gamedone.ogg")
timepointsSound = mixer.Sound("assets/music/songs/timepoints.ogg")
overSound = mixer.Sound("assets/music/songs/gameover.ogg")
timeLowSound = mixer.Sound("assets/music/effects/timeLow.wav")
smallJumpSound = mixer.Sound("assets/music/effects/smallJump.ogg")
bigJumpSound = mixer.Sound("assets/music/effects/bigJump.ogg")
bumpSound = mixer.Sound("assets/music/effects/bump.ogg")
breakSound = mixer.Sound("assets/music/effects/brickBreak.ogg")
coinSound = mixer.Sound("assets/music/effects/coin.ogg")
appearSound = mixer.Sound("assets/music/effects/itemAppear.ogg")
stompSound = mixer.Sound("assets/music/effects/stomp.ogg")
growSound = mixer.Sound("assets/music/effects/grow.ogg")
shrinkSound = mixer.Sound("assets/music/effects/shrink.ogg")
shootSound = mixer.Sound("assets/music/effects/shoot.wav")
# Declaring game functions
def drawScene(background, backX, mario, marioPic, marioFrame, rectList, breakingBrick, brickPic, coins, moveCoins, coinsPic, mushrooms, itemsPic, enemiesList, enemiesPic, bullets, spriteCount, points, isMuted):
"""Function to draw the background, mario, enemies, and all objects"""
X, Y, VX, VY, DIR, STATE = 0, 1, 2, 3, 4, 5
ONGROUND, JUMPFRAMES, INGROUND, ISCROUCH, ONPLATFORM, ISFALLING, ISANIMATING, INVULFRAMES = 0, 1, 2, 3, 4, 5, 6, 7
BRICKVY, IDLE, TYPE = 4, 5, 6
ENMYVX, ENMYVY, ENMYIDLE, ENMYINFLOOR = 4, 5, 6, 7
GUNSTATE, GUNCOUNT, GUNTYPE = 4, 5, 6
BULLVX, BULLVY = 4, 5
screen.fill(BLACK) # Clearing screen
screen.blit(background, (backX, 0)) # Blitting background
# Blitting moving coins
for coin in moveCoins: # Going through each coin and defining rects
coinRect = coin[0], coin[1], coin[2], coin[3]
screen.blit(coinsPic[1][int(spriteCount // 0.4 % 4)], coinRect)
# Blitting mushrooms
for mushroom in mushrooms: # Going through each mushroom and defining rects
mushRect = Rect(mushroom[0], mushroom[1], mushroom[2], mushroom[3])
if mushroom[4] == 0: # Checkiong if the moving up animation is done
screen.blit(itemsPic[0], mushRect)
# Blitting enemies
for list in enemiesList: # For each type of enemy in the enemies list
for enemy in list: # For each individual enemy within that type
enmyRect = Rect(enemy[0], enemy[1], enemy[2], enemy[3])
if list == goombas:
if enemy[ENMYIDLE] == 2: # Checking if enemy is dying
screen.blit(enemiesPic[0][2], enmyRect)
else: # Normal animation
screen.blit(enemiesPic[0][int(spriteCount//6)], enmyRect)
elif list == spinys: # Same thing as goombas except with spinys
spinePic = enemiesPic[2][int(spriteCount// 2.4 % 2)]
if enemy[ENMYVX] > 0: # Checking which direction the enemy is moving (1 or -1)
spinePic = transform.flip(spinePic, True, False)
screen.blit(spinePic, enmyRect)
# Blitting bricks and guns
for list in rectList: # For each type of bricks
for brick in list: # For each individual brick within that type of brick
brickRect = Rect(brick[0], brick[1], brick[2], brick[3]) # Defining the rect of that brick
if list == interactBricks: # Bliting the correct picture if it is an interactBrick
screen.blit(brickPic[1][0],brickRect)
elif list == questionBricks: # Doing the same thing but also checking if the brick has been hit or not
if brick[IDLE] == 1:
screen.blit(brickPic[1][1], brickRect)
else:
screen.blit(brickPic[0][int(spriteCount//2)],brickRect)
elif list == gunRects: # Bliting the pictures for the bullet bills
if brick[GUNTYPE] == 1:
screen.blit(enemiesPic[1][1], (brickRect.x, brickRect.y))
elif brick[GUNTYPE] == 2:
screen.blit(enemiesPic[1][2], (brickRect.x, brickRect.y))
# Blitting brick debris
for brick in breakingBrick: # For each break in all breakable bricks making the debris fall out in all 4 directions if broken
screen.blit(brickPiece[0], (brick[0] - brick[5], brick[1]))
screen.blit(brickPiece[1], (brick[0] + 21 + brick[5], brick[1]))
screen.blit(brickPiece[2], (brick[0] - brick[5] / 2, brick[1] + 21))
screen.blit(brickPiece[3], (brick[0] + 21 + brick[5] / 2, brick[1] + 21))
# Blitting coins
for coin in coins: # For each coin in the list of all coins
coinRect = coin[0], coin[1], coin[2], coin[3] # Defining the coins rect
screen.blit(coinsPic[0][int(spriteCount // 2)], coinRect) # Bliting the coins sprite
# Blitting bullet bills
for bullet in bullets: # going through each bullet and defining the bullets rect
bullRect = Rect(bullet[0], bullet[1], bullet[2], bullet[3])
bullPic = enemiesPic[1][0]
if bullet[BULLVX] > 0:
bullPic = transform.flip(bullPic, True, False)
screen.blit(bullPic, bullRect)
# Blitting flag
screen.blit(flagPic[0],(flagInfo[0][0],flagInfo[0][1])) # Blitting pole
screen.blit(flagPic[1],(flagInfo[1][0],flagInfo[1][1])) # Blitting flag
# Blitting mario
marioShow = marioPic[marioFrame[0]][int(marioFrame[1])]
if mario[DIR] == "Left":
marioShow = transform.flip(marioShow, True, False) # Flipping mario's sprite if he's facing left
if marioStats[INVULFRAMES]%2 == 0 or marioStats[ISANIMATING]: # Checking if mario's sprite should be skipped this frame
screen.blit(marioShow, (mario[0], mario[1])) # Blitting mario's sprite
# Blitting floating points
for point in points:
pointText = marioFontThin.render("%s" %point[3], False, WHITE) # Rendering the text
screen.blit(pointText, (point[0], point[1]))
# Blitting mute icon
if isMuted:
screen.blit(mutePic, (735,25))
def moveBricks(questionBricks, interactBricks, breakingBrick):
""" Function to move all of the bricks when they're in the process of getting hit """
BRICKVY, IDLE, TYPE = 4, 5, 6
# Moving all question blocks that are hit
for brick in questionBricks: # Going through each question block
if brick[BRICKVY] != 3.5 and brick[IDLE] == 1: # Checking if the block is back at its original position or idle
brick[BRICKVY] += 0.5 # Adding VY
brick[1] += brick[BRICKVY] # Applying gravity
# Moving all bricks that are hit and resetting them after
for brick in interactBricks:
if brick[BRICKVY] != 3.5 and brick[IDLE] == 1:
brick[BRICKVY] += 0.5
brick[1] += brick[BRICKVY]
else: # Resetting the brick
brick[BRICKVY] = 0
brick[IDLE] = 0
# Moving all brick debris
for brick in breakingBrick: # Going through all of the debris and adding gravity/motion
brick[1] += brick[4]
brick[4] += 0.8
brick[5] += 3
def floatObjects(moveCoins, points):
""" Function to allow coins and points to float up """
X, Y, COINVY = 0, 1, 4
PTSCOUNT, PTSNUM = 2, 3
for coin in range(len(moveCoins) - 1, -1, -1): # Going through each moving coin
if moveCoins[coin][COINVY] != 5: # Checking if the animation is still going by checking VY
moveCoins[coin][COINVY] += 0.5 # Adding to the VY
moveCoins[coin][Y] += moveCoins[coin][COINVY] # Applying gravity/VY
else: # Deleting coin and adding to points if the animation is finished
points.append([moveCoins[coin][X], moveCoins[coin][Y], 30, 200])
del moveCoins[coin]
for point in range(len(points) - 1, -1, -1): # Going through all points
points[point][PTSCOUNT] -= 1 # Reducing the animation counter
points[point][Y] -= 1 # Moving the text up
def moveItems(rectList, enemiesList, mushrooms, goombas, spinys):
""" Function to check item/enemy collision with the environment and progress animations """
X, Y, DELAY, MOVEUP, MUSHVX, MUSHVY, INFLOOR = 0, 1, 4, 5, 6, 7, 8
ENMYVX, ENMYVY, ENMYIDLE, ENMYINFLOOR = 4, 5, 6, 7
# Making sure all mushrooms are activated
for mushroom in mushrooms: # Going through each mushroom
if mushroom[DELAY] > 0: # Checking if it's being delayed and progressing the counter
mushroom[DELAY] -= 1
elif mushroom[MOVEUP] > 0: # Checking if it's animating and progressing the animation
mushroom[MOVEUP] -= 1
mushroom[1] -= 1
else: # If delay and animation are done, check for its collision
itemCollide(mushroom, rectList, [X, Y, MUSHVX, MUSHVY, INFLOOR])
for goomba in goombas: # Going through each goomba
if goomba[ENMYIDLE] == 1: # Checking if the goomba is active
itemCollide(goomba, rectList, [X, Y, ENMYVX, ENMYVY, ENMYINFLOOR], enemiesList[:2]) # Checking collision
if goomba[ENMYIDLE] == 2: # Checking if the goomba is dying
goomba[ENMYINFLOOR] -=1 # Using the INFLOOR as a counter for removal
for spiny in spinys: # Going through each spiny
if spiny[ENMYIDLE] == 1: # Checking if it's active
itemCollide(spiny, rectList, [X, Y, ENMYVX, ENMYVY, ENMYINFLOOR], enemiesList[:2]) # Checking collision
def itemCollide(item, rectList, indexList, extraCollideIn = None):
""" Function to check item/enemy collision with the floor, objects, and optionally other enemies """
X, Y, VX, VY, INFLOOR = indexList[0], indexList[1], indexList[2], indexList[3], indexList[4]
ENMYVX, ENMYVY, ENMYIDLE, ENMYINFLOOR = 4, 5, 6, 7
extraCollide = copy.deepcopy(extraCollideIn) # Deepcopying to remove mutability
if extraCollide != None: # Checking if there was an input of other rects to collide against
for list in range(len(extraCollide)): # Loop to remove the item itself from the rects to check against
if item in extraCollide[list]:
del extraCollide[list][extraCollide[list].index(item)]
rectList = rectList + extraCollide # Joining the two lists together
# Applying gravity and moving the item across the screen
item[X] += item[VX]
item[VY] += 0.6
item[Y] += item[VY]
itemRect = Rect(item[0] + 3, item[1], item[2] - 3, item[3]) # Declaring rect
# Checking collision with floor and keeping it up
if item[Y] > 496 and not item[INFLOOR]: # If the item is in the floor and not falling through a gap, put it back
item[Y] = 496
item[VY] = 0
try: # Checking if the item is over a gap (using colour collision) and needs to fall
if itemRect.x > 0 and screen.get_at((itemRect.x, itemRect.bottom)) == SKYBLUE and screen.get_at((itemRect.right, itemRect.bottom)) == SKYBLUE:
item[INFLOOR] = True
except IndexError: # If the item is off-screen and an IndexError is raised, ignore
pass
# Floor recovery code
try: # Checking if the item is supposed to be falling but there isn't a gap under them, and stopping their fall
if item[INFLOOR] and screen.get_at((itemRect.x, itemRect.bottom)) != SKYBLUE and screen.get_at((itemRect.right, itemRect.bottom)) != SKYBLUE:
item[INFLOOR] = False
except IndexError: # If the item is off-screen and an IndexError is raised, ignore
pass
itemRect = Rect(item[0], item[1], item[2], item[3]) # Re-declaring react after possible changes
# Going through each rect in the 2D List
for list in rectList:
for brick in list:
brickRect = Rect(brick[0], brick[1], brick[2], brick[3]) # Declaring rect to check against
if itemRect.colliderect(brickRect) and itemRect != brickRect: # Checking the two rects are colliding and that they are not the same (shouldn't collide with itself)
if int(item[Y]) + 3 < brickRect.y: # If the item is above the rect, put it on top (imprecise since it's not needed to be)
item[Y] = brickRect.y - 42
item[VY] = 0
else: # Otherwise change the direction of the item movement (allow it to bounce off)
# Try and except statement to skip the collision if it's a dead enemy
try:
if brick[ENMYIDLE] != 2: # Idle state 2 means dead enemyd
item[VX] *= -1
except IndexError: # If an IndexError is raised, it means it isn't an enemy and collison should work
item[VX] *= -1
def shootBullets(gunRects, bullets, mario):
""" Function to shoot bullets and to move them across the screen """
X, Y, VX, VY, DIR, STATE = 0, 1, 2, 3, 4, 5
GUNSTATE, GUNCOUNT, GUNTYPE = 4, 5, 6
BULLVX, BULLVY = 4, 5
for gun in gunRects: # Going through each gun
if gun[GUNTYPE] == 1 and gun[GUNSTATE] == 1: # Checking if it's the gun or just cosmetic
gun[GUNCOUNT] += 1 # Adding to the shooting counter
if gun[GUNCOUNT] == 180: # If the shooting counter reaches 180
gun[GUNCOUNT] = 0 # Reset the counter
# Checking where Mario is and adjusting bullet stats accordingly
bulletVX = -3
xOffset = -48
if mario[X] > gun[X]:
bulletVX = 3
xOffset = 42
bullets.append([gun[X] + xOffset, gun[Y], 48, 42, bulletVX, 0]) # Append a bullet to the bullets list
playSound(shootSound, "enemy") # Play the bullet shooting sound
for bullet in bullets: # Going through each bullet
if bullet[BULLVY] == 0: # If the bullet has not been stepped on (Checked by seeing if it has no VY)
bullet[X] += bullet[BULLVX] # Move the bullet across the screen according to its VX (Negative or positive)
else: # If the bullet has been stepped on
# Apply gravity
bullet[BULLVY] += 0.6
bullet[Y] += bullet[BULLVY]
def drawStats(mario, marioInfo, points, coins, startTime, level, fastMode, timesUp, coinPic, spriteCount, forceTime = None):
""" Function to draw Mario's stats on screen (Points, coins, level number, and time left) """
ONGROUND, JUMPFRAMES, INGROUND, ISCROUCH, ONPLATFORM, ISFALLING, ISANIMATING, INVULFRAMES = 0, 1, 2, 3, 4, 5, 6, 7
X, Y, VX, VY, DIR, STATE = 0, 1, 2, 3, 4, 5
currentTime = 200 - int((time.get_ticks() - startTime) / 1000) # Getting the time in seconds
if forceTime != None: # If a forced time was entered, ignore the calculated time and use the forced time
currentTime = forceTime
# Time checks (ignored if a forced time is entered)
if currentTime < 100 and not fastMode and forceTime is None: # If the time is lower than 100 and fast mode hasn't been activated
playSound(timeLowSound, "music") # Play the "time low" sound
playSound(backgroundFastSound, "music", True) # Queue the sped up background music
fastMode = True # Set fast mode to on
if currentTime == 0 and not timesUp and forceTime is None: # If the time is 0 and the times up animation hasn't been played
currentTime = 0 # Set the current time to zero
marioInfo[ISANIMATING] = True # Make Mario animate
mario[STATE] = -1 # Set his state to -1 (-1 means dead)
timesUp = True # Set times up to on
# Rendering text
points = marioFontBig.render("%06i" %int(points), False, (255,255,255))
coins = marioFontBig.render("x%02i" %int(coins), False, (255,255,255))
world = marioFontBig.render("1-%i" %int(level), False, (255,255,255))
timer = marioFontBig.render("%03i" %int(currentTime), False, (255,255,255))
# Blitting text and coin sprite
screen.blit(points, (75,50))
screen.blit(marioText, (75,25))
screen.blit(coins, (300,50))
screen.blit(worldText, (450,25))
screen.blit(world, (470,50))
screen.blit(timeText, (625,25))
screen.blit(timer, (640, 50))
screen.blit(coinPic[int(spriteCount//2)], (275,48))
return fastMode, timesUp # Returning fast mode and time up values (Which could stay the same)
def drawPause():
""" Function to draw the pause screen on top of the surface """
alphaSurface = Surface((800, 600)) # Making a surface
alphaSurface.set_alpha(128) # Giving it alpha functionality
alphaSurface.fill((0, 0, 0)) # Fill the surface with a black background
screen.blit(alphaSurface, (0, 0)) # Blit it into the actual screen
# Blitting pause screen text
screen.blit(pauseText, (345,290))
screen.blit(helpText, (210, 330))
def moveSprites(mario, marioInfo, frame, forceTime = None):
""" Function to cycle through Mario's sprites """
X, Y, VX, VY, DIR, STATE = 0, 1, 2, 3, 4, 5
ONGROUND, JUMPFRAMES, INGROUND, ISCROUCH, ONPLATFORM, ISFALLING, ISANIMATING, INVULFRAMES = 0, 1, 2, 3, 4, 5, 6, 7
changingSprites = [[2,5], [2,4], [2,5], [2,4], [2,5], [2,4], [1,0]] # List of Mario's change state sprites (Indices)
isDead = False # Boolean to see if the level should restart (returned and handled by game function)
# Mario's animation loop
if marioInfo[ISANIMATING]: # If mario is animating
# Animation counter checks
if mario[STATE] == -1: # If his state is -1 (dead)
forceTime = 0 # Force the time to be zero
if frame[2] > 70: # If his animation counter has reached 70
isDead = True # Restart the level
if frame[2] == 55: # If his animation counter has reached 55
marioInfo[ISANIMATING] = False # Turn off his animation
frame[2] = 0 # Reset the animation counter
if mario[STATE] == 0: # If his result state should be 0 (small mario)
mario[Y] += 42 # Shift mario down
frame[0], frame[1] = 0, 0 # Reset his sprite frame to standing
return isDead, forceTime # Ignore the rest of the function and return
if mario[STATE] == -1 and frame[2] == 0: # If mario is dying and his animation counter hasn't been touched
frame[0], frame[1] = 2, 3 # Set his sprite to dying
frame[2] = -15 # Turn the animation counter into a VY starting at -15
playSound(deathSound, "music") # Start the death music
elif mario[STATE] == -1: # If he's dying but his animation counter has touched (meaning above has already ran)
frame[0], frame[1] = 2, 3 # Keep his dying sprite on
frame[2] += 0.4 # Add to his VY
if frame[2] > -9: # If his VY has passed -9, then apply gravity (Acts as a delay to his falling animation)
mario[Y] += frame[2]
elif mario[STATE] == 0: # If his result state should be 0 (small Mario)
# Reverse the animation sprite List
for index in range(len(changingSprites)):
if changingSprites[index] == [2,5]:
changingSprites[index] = [1,0]
elif changingSprites[index] == [1,0]:
changingSprites[index] = [2,5]
# Add to his animation counter and apply the counter value to the current sprite
frame[2] += 1
frame[0], frame[1] = changingSprites[(frame[2]//8)][0], changingSprites[(frame[2]//8)][1]
elif mario[STATE] == 1: # Same as above but without the list reversal
frame[2] += 1
frame[0], frame[1] = changingSprites[(frame[2]//8)][0], changingSprites[(frame[2]//8)][1]
return isDead, forceTime # Stop running the function and return isDead/forceTime (Which could stay unchanged)
# Mario's normal movement loop
if marioInfo[ONGROUND]:
frame[0] = 0 + mario[STATE] # Adjusting for sprite for big mario
# Mario's running sprite counter
if frame[1] < 3.8 and mario[VY] < 2: # If the counter hasn't reached to upper limit and Mario isn't falling too fast
frame[1] += mario[VX]**2/100 + 0.2 # Taking his VX and adding to counter accordingly
else: # Mario's sprite if he's falling fast
frame[1] = 1
if frame[1] > 3.9: # Sprite counter upper limit
frame[1] = 3.9
if mario[VX] == 0: # If mario isn't moving, stay on his standing sprite
frame[1] = 0
else:
frame[0],frame[1] = 2, 0 + mario[STATE] # If mario is midair, stay on his jumping sprite
if marioInfo[ISCROUCH]:
frame[0],frame[1] = 2, 2 # If mario is crouching, stay on his crouching sprite
if marioInfo[INVULFRAMES] != 0: # Loop to reduce Mario's invulnerability counter
marioInfo[INVULFRAMES] -= 1
def checkMovement(mario, marioInfo, acclerate, rectLists, pressSpace, clearRectList):
"""Function to accept inputs and apply the appropriate physics """
keys = key.get_pressed()
X, Y, VX, VY, DIR, STATE = 0, 1, 2, 3, 4, 5
ONGROUND, JUMPFRAMES, INGROUND, ISCROUCH, ONPLATFORM, ISFALLING, ISANIMATING = 0, 1, 2, 3, 4, 5, 6
moving = False # Variable to check if user is moving Mario
# Walking logic
if keys[K_a] and keys[K_d]: # If both keys are pressed, don't move
mario[VX] = 0
elif keys[K_a] and not marioInfo[ISCROUCH]: # Checking if user is pressing left and not crouching
if mario[DIR] != "Left":
mario[VX] = 0 # Stop acceleration if changing direction
walkMario(mario, rectLists, "Left", clearRectList)
moving = True
mario[DIR] = "Left"
elif keys[K_d] and not marioInfo[ISCROUCH]: # Checking if user is pressing right and not crouching
if mario[DIR] != "Right":
mario[VX] = 0 # Stop acceleration if changing direction
walkMario(mario, rectLists, "Right", clearRectList)
moving = True
mario[DIR] = "Right"
if keys[K_s] and mario[STATE]==1: # Allow crouching if big mario is active
marioInfo[ISCROUCH]=True
if mario[STATE]==0 and marioInfo[ISCROUCH]: # Don't allow small mario to be in crouching position
marioInfo[ISCROUCH]=False
if moving: # Accelerate if there is input
if marioInfo[ONGROUND]:
mario[VX] += acclerate
else:
mario[VX] += acclerate/4 # Slow down movement when midair
elif mario[VX] != 0: # Move and decelerate if there is no input
if mario[DIR] == "Right":
walkMario(mario, rectLists, "Right", clearRectList)
if mario[DIR] == "Left":
walkMario(mario, rectLists, "Left", clearRectList)
if marioInfo[ONGROUND]: # Don't decelerate mid air
mario[VX] -= acclerate
# Max and min acceleration
if mario[VX] > 5:
mario[VX] = 5
elif mario[VX] < 0:
mario[VX] = 0
# Jumping logic
gravity = 0.6
floor = 496 # The floor Y coords (Using top of sprite)
marioOffset = 42 # Height of sprite
if mario[STATE]==1: # Change values if mario is big
floor=452
marioOffset = 88
if marioInfo[ISCROUCH]: # If mario is crouching, give him more gravity
gravity = 0.9
if marioInfo[ONPLATFORM] and mario[VY] <= gravity*2 and pressSpace: # If mario is on a platform and pressing space, let him jump
marioInfo[ISFALLING] = False
marioInfo[ONPLATFORM] = False
if keys[K_SPACE] and not marioInfo[ISCROUCH] and not marioInfo[ONPLATFORM]:
if marioInfo[ONGROUND] and pressSpace: # Checking if Mario is on ground and this is users first key down
mario[VY] = -9.5 # Jumping power
marioInfo[ONGROUND] = False
marioInfo[JUMPFRAMES] = 0 # Resetting the higher jump counter
# Playing jumping sounds
if mario[STATE] == 0:
playSound(smallJumpSound, "effect")
else:
playSound(bigJumpSound, "effect")
elif marioInfo[JUMPFRAMES] < 41 and not marioInfo[ISFALLING] and not marioInfo[ONPLATFORM]: # Simulating higher jump with less gravity
gravity = 0.2
marioInfo[JUMPFRAMES] += 1 # Adding to the higher jump counter
mario[Y] += mario[VY] # Add the y movement value
if not marioInfo[INGROUND] and mario[Y]>=floor and screen.get_at((int(mario[X]+4),int(mario[Y]+marioOffset)))==SKYBLUE and \
screen.get_at((int(mario[X]+38),int(mario[Y]+marioOffset)))==SKYBLUE:
# Using colour collision to fall through holes
marioInfo[INGROUND] = True
marioInfo[ONGROUND] = False
elif mario[Y] >= floor and not marioInfo[INGROUND]: # Checking floor collision
mario[Y] = floor # stay on the ground
mario[VY] = 0 # stop falling
marioInfo[ONGROUND] = True
marioInfo[ONPLATFORM] = False
marioInfo[ISFALLING] = False
# Try except statement for Mario's falling recovery
try:
if marioInfo[INGROUND] and screen.get_at((int(mario[X]+4),int(mario[Y]+marioOffset)))!=SKYBLUE and \
screen.get_at((int(mario[X]+38),int(mario[Y]+marioOffset)))!=SKYBLUE: # Allow mario to recover if he goes back above the ground
marioInfo[INGROUND] = False
except IndexError: # If IndexError is raised, ignore
pass
if marioInfo[INGROUND] and mario[Y] > 700: # If Mario falls off the screen, play his dying animation
marioInfo[ISANIMATING] = True # Set animating to True
mario[STATE] = -1 # Put his state to -1 (dead)
marioPos[VY] += gravity # apply gravity
def walkMario(mario, rectLists, direction, clearRectList):
""" Function to move the player, background, and all rectangles """
X, Y, VX, VY, DIR, STATE = 0, 1, 2, 3, 4, 5
global backPos
# Checking which direction to move
if direction == "Left" and mario[X] != 1:
mario[X] -= mario[VX] # Subtracting the VX
elif direction == "Right":
if mario[X] < 368: # Checking if mario is in the middle of the screen
mario[X] += mario[VX] # Adding the VX
else:
mario[X] = 368
backPos -= mario[VX] # Subtracting the VX from the background
# Moving all rectangles
for subList in range(len(rectLists)):
for rect in range(len(rectLists[subList])):
rectLists[subList][rect][0] -= mario[VX]
for subList in range(len(clearRectList)):
for rect in range(len(clearRectList[subList])):
clearRectList[subList][rect][0] -= mario[VX]
if mario[X] < 0:
mario[X] = 0
def checkCollide(mario, marioInfo, marioScore, rectLists, breakingBrick, moveCoins, mushrooms):
""" Function to check mario's collision with Rects """
X, Y, VX, VY, DIR, STATE = 0, 1, 2, 3, 4, 5
ONGROUND, JUMPFRAMES, INGROUND, ISCROUCH, ONPLATFORM, ISFALLING = 0, 1, 2, 3, 4, 5
BRICKVY, IDLE, TYPE = 4, 5, 6
PTS, COIN, LIVES = 0, 1, 2
# Finding and declaring Mario's Rect
height = 42
if mario[STATE] == 1:
height = 84
originalMarioRect = Rect(mario[X] + 2, mario[Y], 38 - 2, height) # Mario's Rect
originalX, originalY, originalVY = mario[X], mario[Y], mario[VY] # Storing orignial movement values
hitBrick = [] # List to store all bricks that were hit
# Going through each rect
for list in rectLists:
for brick in list:
brickRect = Rect(brick[0], brick[1], brick[2], brick[3]) # Defining the brick Rect
marioRect = Rect(mario[X] + 2, mario[Y], 38 - 2, height) # Mario's hit box (and making it a little smaller)
if brickRect.colliderect(marioRect):
if int(mario[Y]) + height - int(mario[VY]) - 1 <= brickRect.y: # Hitting top collision
marioInfo[ONGROUND] = True
marioInfo[ONPLATFORM] = True
marioInfo[ISFALLING] = True
mario[VY] = 0 # Stopping his fall
mario[Y] = brickRect.y - height # Forcing him out of the rect upwards
elif int(mario[Y] - mario[VY]) >= int(brickRect.y + brickRect.height): # Hitting bottom collision
mario[VY] = 1 # Pushing him down
mario[Y] = brickRect.y + brickRect.height # Forcing him out of the rect downwards
marioInfo[JUMPFRAMES] = 41 # Locking his jump
elif int(mario[X] + 2) >= int(brickRect[X]): # Right side collision
mario[X] = brickRect.x + brickRect.width - 2 # Move mario to the right of the rect
mario[VX] = 0
elif int(mario[X] + 2) <= int(brickRect[X]): # Left side collision
mario[X] = brickRect.x - 38 # Move mario to the left of the rect
mario[VX] = 0
# Checking if Mario was originall colliding with the rect and if it was interactable
if list != brickList and brickRect.colliderect(originalMarioRect) and originalY - originalVY >= brickRect.y + brickRect.height:
hitBrick.append([brick, list])
# Going through each interactable brick that he hit
for list in hitBrick:
brick, type = list[0], list[1] # Splitting up the list
brickRect = Rect(brick[0], brick[1], brick[2], brick[3]) # Defining the Rect
# Handling collision with multiple bricks
if len(hitBrick) != 1:
if abs(brickRect.x - originalX) > 21:
continue # Skipping this brick
else:
del hitBrick[-1] # Skipping the next brick
# Manipulating bricks appropriately
if type == interactBricks and brick[IDLE] == 0: # If the rect is a brick and is idle
indexBrick = interactBricks.index(brick) # Find it in the bricks list
if brick[TYPE] > 0 or mario[STATE] == 0: # If Mario is small or there are coins in the brick
interactBricks[indexBrick][BRICKVY] = -4 # Add VY to the brick
interactBricks[indexBrick][IDLE] = 1 # Make it non-idle
playSound(bumpSound, "effect") # Play the bump sound
if brick[TYPE] > 0: # If the brick has coins
brick[TYPE] -= 1 # Remove one
moveCoins.append([interactBricks[indexBrick][0] + 6, interactBricks[indexBrick][1], 30, 32, -12]) # Append a coin to the moving coins list
playSound(coinSound, "block") # Play the coin sound
# Adding to the score
marioScore[COIN] += 1
marioScore[PTS] += 200
if brick[TYPE] == 0: # If the brick is now empty
questionBricks.append([brick[0], brick[1], brick[2], brick[3], brick[4], brick[5], 0]) # Add it to the question blocks list and make it non-interactable
del interactBricks[indexBrick] # Delete the brick
else: # If Mario is big or there are no coins
interactBricks[indexBrick][BRICKVY] = -9 # Add VY to it
breakingBrick.append(interactBricks[indexBrick]) # Append it to the breaking bricks list
del interactBricks[indexBrick] # Delete it from the bricks list
playSound(breakSound, "block") # Play bumping sound
elif type == questionBricks: # If the rect is a question block
playSound(bumpSound, "effect") # Play bumping sound
if brick[IDLE] == 0: # If the block is idle
indexBrick = questionBricks.index(brick) # Find it in the question blocks list
questionBricks[indexBrick][IDLE] = 1 # Set the brick to non-interactable
questionBricks[indexBrick][BRICKVY] = -4 # Give the block VY
if questionBricks[indexBrick][TYPE] == 1: # If the block has a coin
moveCoins.append([questionBricks[indexBrick][0] + 6, questionBricks[indexBrick][1], 30, 32, -12])
playSound(coinSound, "block") # Play the coin sound
# Add to the score
marioScore[COIN] += 1
marioScore[PTS] += 200
elif questionBricks[indexBrick][TYPE] == 2: # If the block has a mushroom
mushrooms.append([questionBricks[indexBrick][0], questionBricks[indexBrick][1], 42, 42, 15, 42, 3, 0, False]) # Append a mushroom to the mushroom list
playSound(appearSound, "block") # Play the mushroom sound
def checkClearCollide(mario, marioStats, marioScore, coins, mushrooms, enemiesList, points, bullets, startTime):
""" Function to check Mario's collision with objects that he can pass through """
PTS, COIN, LIVES = 0, 1, 2
X, Y, VX, VY, DIR, STATE = 0, 1, 2, 3, 4, 5
ONGROUND, JUMPFRAMES, INGROUND, ISCROUCH, ONPLATFORM, ISFALLING, ISANIMATING, INVULFRAMES = 0, 1, 2, 3, 4, 5, 6, 7
X, Y, DELAY, MOVEUP, MUSHVX, MUSHVY = 0, 1, 4, 5, 6, 7
ENMYVX, ENMYVY, ENMYIDLE, ENMYINFLOOR = 4, 5, 6, 7
BULLVX, BULLVY = 4, 5
# Declaring Mario's hitbox
height = 42
if mario[STATE] == 1:
height = 84
marioRect = Rect(mario[X], mario[Y], 38 - 2, height) # Declaring Mario's Rect
if marioStats[ISCROUCH]: # Shortening Mario hit box if he's crouching
marioRect = Rect(mario[X], mario[Y] + 42, 38 - 2, 42)
# Going through each coin
for coin in range(len(coins) - 1, -1, -1):
coinRect = Rect(coins[coin][0], coins[coin][1], coins[coin][2], coins[coin][3]) # Declaring coin rect
if marioRect.colliderect(coinRect): # Checking if they're colliding
del coins[coin] # Deleting the coin from the list
playSound(coinSound, "block") # Playing the coin sound
# Adding to score
marioScore[PTS] += 200
marioScore[COIN] += 1
points.append([coinRect.x, coinRect.y, 30, 200]) # Appending to the points list
# Going through each mushroom
for index in range(len(mushrooms) - 1, -1, -1):
mushRect = Rect(mushrooms[index][0], mushrooms[index][1], mushrooms[index][2], mushrooms[index][3]) # Declaring mushroom rect
if marioRect.colliderect(mushRect) and mushrooms[index][DELAY] == 0 and mushrooms[index][MOVEUP] == 0: # Checking if Mario is colliding and the mushroom is not animating
if mario[STATE] == 0: # If Mario's state is 0 (small)
mario[Y] -= 42 # Shift Mario up
mario[STATE] = 1 # Change his state to 1 (big)
marioStats[ISANIMATING] = True # Make him animate
playSound(growSound, "effect") # Play the animating sound
marioScore[PTS] += 2000 # Add to points
points.append([mushRect.x, mushRect.y, 30, 2000]) # Append to points list
del mushrooms[index] # Delete mushroom from list
# Going through each enemy in the 2D list
for list in range(len(enemiesList)):
for enemy in range(len(enemiesList[list]) - 1, -1, -1):
# Defining hitbox depending on enemy
if enemiesList[list] == goombas or enemiesList[list] == spinys:
enmyRect = Rect(enemiesList[list][enemy][0] + 5, enemiesList[list][enemy][1] + 10, enemiesList[list][enemy][2] - 10, enemiesList[list][enemy][3] - 10)
elif enemiesList[list] == bullets:
enmyRect = Rect(enemiesList[list][enemy][0] + 15, enemiesList[list][enemy][1], enemiesList[list][enemy][2] - 15, enemiesList[list][enemy][3])
# Checking if there is collision and if the enemies are properly active/should collide
if marioRect.colliderect(enmyRect) and ((enemiesList[list] == goombas and enemiesList[list][enemy][ENMYIDLE] != 2) or (enemiesList[list] == bullets and enemiesList[list][enemy][BULLVY] == 0) or (enemiesList[list] == spinys)):
# Checking top collision (Ignoring spinys since they don't have a top collision)
if int(mario[Y]) + height - int(mario[VY]) <= enmyRect.y and enemiesList[list] != spinys:
mario[VY] = -7.5 # Setting Mario's VY to -7.5
marioStats[ISFALLING] = True # Set falling to true
marioStats[ONGROUND] = False
marioScore[PTS] += 100 # Add to points
playSound(stompSound, "effect") # Play the stomp sound
if enemiesList[list] == goombas: # If the enemy is a goomba
enemiesList[list][enemy][ENMYIDLE] = 2 # Set the enemy state to 2 (dead)
enemiesList[list][enemy][ENMYINFLOOR] = 32 # Turning the infloor value into a counter for removing dead goombas
elif enemiesList[list] == bullets: # If enemy is a bullets
enemiesList[list][enemy][BULLVY] = -1 # Set the VY to -1 (Once it tips over, it keeps going)
points.append([enmyRect.x, enmyRect.y, 30, 100]) # Appending to the points list
# If you don't hit the top, you get damaged
elif marioStats[INVULFRAMES] == 0: # If mario is not invulnerable
mario[STATE] -= 1 # Reducing Mario's state by 1
marioStats[ISANIMATING] = True # Animating Mario
if mario[STATE] == 0: # If Mario's state is now 0 (small)
marioStats[INVULFRAMES] = 80 # Setting
playSound(shrinkSound, "effect")
# Checking victory pole collision
isPole = False # Boolean to see if Mario is on the pole
forceTime = None #
poleRect = Rect(flagInfo[0][0], flagInfo[0][1], flagInfo[0][2], flagInfo[0][3]) # Declaring the pole Recr
if marioRect.colliderect(poleRect): # If mario collides with the pole
isPole = True
mario[X] = poleRect.x - 16 # Making mario slide down
playSound(flagSound, "music") # Playing the flag sound
forceTime = 200 - (time.get_ticks() - startTime)//1000
return [isPole, forceTime] # Returning values for game function to handle
def movePole(mario, marioStats, marioScore, frame, flagInfo, unisprite, isDone, forceTime):
""" Function for the pole grabbing animation """
PTS, COIN, LIVES = 0, 1, 2
X, Y, VX, VY, DIR, STATE = 0, 1, 2, 3, 4, 5
ONGROUND, JUMPFRAMES, INGROUND, ISCROUCH, ONPLATFORM, ISFALLING, ISANIMATING, INVULFRAMES = 0, 1, 2, 3, 4, 5, 6, 7
# Declaring flag pole Rects
poleRect = Rect(flagInfo[0][0], flagInfo[0][1], flagInfo[0][2], flagInfo[0][3])
flagRect = Rect(flagInfo[1][0], flagInfo[1][1], flagInfo[1][2], flagInfo[1][3])
offset = 0 # Offset for height
endPoint = 650 # Variable for the X coordinate of the castle door
if levelNum == 5: # If the level is 5, change the castle door position
endPoint = 690
if mario[STATE] == 1: # If Mario is big, change the offset
offset = 42
if flagRect.y < 451: # If the flag Y position is not past 451, bring it lower
flagInfo[1][1] += 4
if mario[Y] < 451 - offset: # If Mario is not att the bottom the pole
mario[Y] += 4 # Bring him lower
frame[0], frame[1] = 3, (unisprite * 0.8 % 2 + mario[STATE] * 2) # Rotate is sprite
if mario[Y] >= 451 - offset and flagRect.y >= 451 and frame[2] < 20: # If both Mario and the flag are at the bottom, and the animation counter hasn't reached 20
mario[X] = poleRect.x + 16 # Put Mario on the other side of the pole (still holding)
frame[0], frame[1] = 3, 4 + mario[STATE] # Keep his sprite of the grabbing pole sprite
frame[2] += 1 # Add to the animation counter
elif mario[Y] >= 451 - offset and flagRect.y >= 451 and frame[2] == 20: # If both Mario and the flag are at the bottom, and the animation counter is at 20
mario[X] = poleRect.right # Put Mario to the left of the pole
mario[Y] = 496 - offset # Put Mario on the ground, according to Mario's state
frame[2] += 1 # Add to the frame counter
if levelNum != 5: # If the level is 5, play the game done sound
playSound(doneSound, "music")
else: # If it's any other level, play the level done sound
playSound(doneworldSound, "music")
elif mario[Y] >= 451 - offset and flagRect.y >= 451: # If both the above are done
mario[VX] = 5 # Make Mario's speed 5
mario[VY] = 0 # Make his VY 0
mario[X] += 3.5 # Keep moving Mario right
marioStats[ONGROUND] = True
moveSprites(mario,marioStats,frame) # Use the moveSprites function to rotate sprites
if mario[X] > endPoint and mario[X] < 800: # If Mario reaches the castle and hasn't been moved yet
mario[X] = 800 # Move Mario off screen
playSound(timepointsSound, "effect") # Play the time to points conversion sound
if mario[X] > 800 and forceTime != 0: # If Mario has been moved off screen (meaning the time conversion has started)
forceTime -= 1 # Remove a second from the forced time
marioScore[PTS] += 100 # Add 100 to Marios points for each second removed
if forceTime == 0: # If the force time has reached zero (meaning the time conversion is done)
mixer.Channel(1).stop() # Stop the conversion sound
if not mixer.Channel(0).get_busy(): # If the end level sound is done, go to the next level
isDone = True
return [isDone, forceTime] # Return the isDone and forced time values for the game function to handle
def rotateRect(rectList, breakingBrick, itemsList, enemiesList, bullets, gunsList, points):
""" Function to take the activate and deactivate Rects relative to the screen """
X, Y, ENMYVX, ENMYVY, ENMYIDLE, ENMYINFLOOR = 0, 1, 4, 5, 6, 7
GUNSTATE, GUNCOUNT, GUNTYPE = 4, 5, 6
# Deleting any offscreen Rects
for index in range(len(breakingBrick) - 1, -1, -1): # Going through all of the brick debris and deleting them once they exit below the screen
if breakingBrick[index][1] > 600:
del breakingBrick[index]
for list in range(len(itemsList)): # Going through all of the items and deleting them once they scroll off the screen
for item in range(len(itemsList[list]) - 1, -1, -1):
if itemsList[list][item][0] < -300 or itemsList[list][item][1] > 650:
del itemsList[list][item]
for list in range(len(rectList)): # Going through all of the rects and deleting them once they scroll off the screen
for rect in range(len(rectList[list]) - 1, -1, -1):
if rectList[list][rect][0] < -700:
del rectList[list][rect]
for list in range(len(enemiesList)): # Going through all of the enemies and deleting them once they scroll off the screen
for rect in range(len(enemiesList[list]) - 1, -1, -1):
if enemiesList[list][rect][0] < -300 or enemiesList[list][rect][1] > 650:
del enemiesList[list][rect]
for point in range(len(points) - 1, -1, -1): # Going through all of the points indicators and deleting them once the counter reaches zero
if points[point][2] == 0:
del points[point]
# Activating and deactivating all enemies
for list in range(len(enemiesList)): # Going through all of the enemies
for enemy in range(len(enemiesList[list]) - 1, -1, -1):
if enemiesList[list] == goombas or enemiesList[list] == spinys: # If they are goombas or spinys
# Activating goombas and spinys if they get close to the screen
if enemiesList[list][enemy][ENMYIDLE] == 0 and enemiesList[list][enemy][X] < 800:
enemiesList[list][enemy][ENMYIDLE] = 1
# Deleting them if they are crushed by mario and the death counter reaches zero
elif enemiesList[list][enemy][ENMYIDLE] == 2 and enemiesList[list][enemy][ENMYINFLOOR] == 0:
points.append([enemiesList[list][enemy][0], enemiesList[list][enemy][1], 40, 100])
del enemiesList[list][enemy]
elif enemiesList[list] == bullets: # If they are bullets
# Deleting bullets if they are too far off screen
if enemiesList[list][enemy][0] < -1600 or enemiesList[list][enemy][0] > 1600:
del enemiesList[list][enemy]
for gun in range(len(gunsList) - 1, -1, -1): # Going through all of the guns
# Activating guns if they get close and deleting them if they get too far back
if gunsList[gun][0] < 1600:
gunsList[gun][GUNSTATE] = 1
if gunsList[gun][0] < -1600:
del gunsList[gun]
def playSound(soundFile, soundChannel, queue = False):
""" Function to load in sounds and play them on a channel """
channelList = [["music", 0], ["effect", 1], ["block", 2], ["extra", 3], ["enemy", 4]] # List to keep track of mixer channels
for subList in channelList: # For loop to identify the input through corresponding lists
if subList[0] == soundChannel:
channelNumber = subList[1] # Taking the matching channel number and declaring it
if queue: # If the input requests for a queue
mixer.Channel(channelNumber).queue(soundFile) # Add the sound to the queue
else:
mixer.Channel(channelNumber).stop() # Stopping any previous sound
mixer.Channel(channelNumber).play(soundFile) # Playing new sound
def globalSound(command):
""" Function to apply commands to all mixer channels """
for id in range(mixer.get_num_channels()): # Going through each mixer channel
if command == "stop":
mixer.Channel(id).stop() # Stopping all playback on the channel
elif command == "pause":
mixer.Channel(id).pause() # Pausing playback on the channel
elif command == "unpause":
mixer.Channel(id).unpause() # Unpausing playback on the channel
elif command == "toggleVol":
if mixer.Channel(id).get_volume() == 0: # Checking if the channel is muted
mixer.Channel(id).set_volume(1) # Unmuting the channel
else:
mixer.Channel(id).set_volume(0) # Otherwise, mute the channel
def spriteCounter(counter):
""" Function to progress the universal sprite counter"""
counter += 0.2 # Adding to the counter
if counter > 10: # Checking if the counter hits the limit and resetting it
counter = 0
return counter # Returning the new counter
# Declaring loading functions
def loadFile(targetFile):
""" Function to load files and make lists out of them"""
outputList = []
file = open(targetFile, "r") # Loading file
fileLines = file.readlines() # Splitting into lines
for line in fileLines:
line = line.strip("\n") # Removing any line separators
line = line.split(",") # Dividing elements separated by commas
listLength = len(line)
outputList.append([int(line[index]) for index in range(listLength)]) # Appending line info to list
return outputList # Returning final list
# Declaring main functions
def game():
""" Main game function """
running = True
X, Y, VX, VY, DIR, STATE = 0, 1, 2, 3, 4, 5
ONGROUND, JUMPFRAMES, INGROUND, ISCROUCH, ONPLATFORM, ISFALLING, ISANIMATING = 0, 1, 2, 3, 4, 5, 6
PTS, COIN, LIVES = 0, 1, 2
global levelNum
playSound(backgroundSound, "music") # Starting the background music
# Declaring session specific booleans
pausedBool = False # Boolean to keep track of pause status
isMuted = False # Boolean to see if the sound is muted
if mixer.Channel(0).get_volume() == 0: # Checking current volume and fixing mute boolean accordingly
isMuted = True
timesUp = False # Boolean to see if the game timer has reached zero
isPole = False # Boolean to see if Mario is animating on the victory pole
isDead = False # Boolean to see if the game should reload the level after death
isDone = False # Boolean to see if the game should load the next level after victory
fast = False # Boolean to see if the music is playing faster
uniSprite = 0 # Counter to control all non - Mario sprites
forceTime = None # Variable to keep track of a time that should be forced on screen
# Declaring session specific lists
breakingBrick = [] # List of bricks that are broken and showing their debris
moveCoins = [] # List of animating coins that Mario has grabbed
mushrooms = [] # List of mushroom powerups
bullets = [] # List of bullet bill enemies
points = [] # List of animating point indicators
# Declaring packaged lists
rectList = [brickList, interactBricks, questionBricks, gunRects] # List of rectangles Mario can't pass through
clearRectList = [coins, moveCoins, breakingBrick, mushrooms, goombas, points, flagInfo, bullets, spinys] # List of rectangles Mario can pass through
itemsList = [mushrooms] # List of all items
enemiesList = [goombas, spinys, bullets] # List of all enemies
startTime = time.get_ticks() # Variable to keep track of time since level start
while running:
mx, my = mouse.get_pos()
initialSpace = False
for evnt in event.get():
if evnt.type == QUIT:
return "exit"
if evnt.type == KEYDOWN:
# Keep track of when the user first presses space
if evnt.key == K_SPACE:
initialSpace = True
# Toggling the music volume on or off
elif evnt.key == K_m:
isMuted = not isMuted
globalSound("toggleVol")
# Toggling the paused status
elif evnt.key == K_p:
pausedBool = not pausedBool
if pausedBool:
globalSound("pause")
playSound(pauseSound, "extra")
pauseTime = time.get_ticks() - startTime
else:
globalSound("unpause")
# Exiting the game
elif evnt.key == K_ESCAPE and pausedBool:
return "menu"
elif evnt.type == KEYUP:
# Keep track of when user lets go of space
if evnt.key == K_SPACE:
marioStats[ISFALLING] = True
# Keep track of when user lets go of the crouch key
elif evnt.key== K_s: