├── .gitignore ├── README.md ├── classes ├── Animation.py ├── Camera.py ├── Collider.py ├── Dashboard.py ├── EntityCollider.py ├── Font.py ├── GaussianBlur.py ├── Input.py ├── Level.py ├── Maths.py ├── Menu.py ├── Pause.py ├── Sound.py ├── Sprite.py ├── Sprites.py ├── Spritesheet.py ├── Tile.py └── __init__.py ├── compile.py ├── entities ├── Coin.py ├── EntityBase.py ├── Goomba.py ├── Item.py ├── Koopa.py ├── Mario.py ├── RandomBox.py └── __init__.py ├── img ├── 1.PNG ├── 2.PNG ├── 3.PNG ├── 4.PNG ├── Items.png ├── characters.gif ├── font.png ├── koopas.png ├── pics.png ├── pictures.PNG ├── tiles.png ├── title_screen.png └── yoshis.png ├── levels ├── Level1-1.json └── Level1-2.json ├── main.py ├── requirements.txt ├── sfx ├── __init__.py ├── bump.ogg ├── coin.ogg ├── death.wav ├── kick.ogg ├── main_theme.ogg ├── powerup.ogg ├── powerup_appears.ogg ├── small_jump.ogg └── stomp.ogg ├── sprites ├── Animations.json ├── BackgroundSprites.json ├── Goomba.json ├── ItemAnimations.json ├── Koopa.json └── Mario.json └── traits ├── __init__.py ├── bounce.py ├── go.py ├── jump.py └── leftrightwalk.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.vscode 3 | /dist 4 | settings.json 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Super Mario Implementation in Python 2 | 3 | ## Creator 4 | 5 | Nirankar Nath Singh 6 | 7 | Please Subscribe My Youtube channel 8 | 9 | Name : Brain Flicks 10 | 11 | Link : https://www.youtube.com/nirankarnathsingh 12 | 13 | Instagram : https://www.instagram.com/bnirankar 14 | ## Running 15 | 16 | * $ pip install -r requirements.txt 17 | * $ python main.py 18 | 19 | ## Standalone windows build 20 | 21 | * $ pip install py2exe 22 | * $ python compile.py py2exe 23 | 24 | ## Controls 25 | 26 | * Left: Move left // Left Arrow 27 | * Right: Move right //Right Arror 28 | * Space: Jump //Up Arror 29 | * Shift: Boost 30 | * Left/Right Mouseclick: Secret 31 | 32 | ## Current state: 33 | ![Alt text](img/pics.png "current state") 34 | 35 | ## Dependencies 36 | * pygame 37 | * scipy 38 | 39 | ## Contribution 40 | 41 | If you have any Improvements/Ideas/Refactors feel free to contact me or make a Pull Request. 42 | The code needs still alot of refactoring as it is right now, so I appreciate any kind of Contribution. 43 | -------------------------------------------------------------------------------- /classes/Animation.py: -------------------------------------------------------------------------------- 1 | class Animation: 2 | def __init__(self, images, idleSprite=None, airSprite=None, deltaTime=7): 3 | self.images = images 4 | self.timer = 0 5 | self.index = 0 6 | self.image = self.images[self.index] 7 | self.idleSprite = idleSprite 8 | self.airSprite = airSprite 9 | self.deltaTime = deltaTime 10 | 11 | def update(self): 12 | self.timer += 1 13 | if self.timer % self.deltaTime == 0: 14 | if self.index < len(self.images) - 1: 15 | self.index += 1 16 | else: 17 | self.index = 0 18 | self.image = self.images[self.index] 19 | 20 | def idle(self): 21 | self.image = self.idleSprite 22 | 23 | def inAir(self): 24 | self.image = self.airSprite 25 | -------------------------------------------------------------------------------- /classes/Camera.py: -------------------------------------------------------------------------------- 1 | from classes.Maths import vec2D 2 | 3 | 4 | class Camera: 5 | def __init__(self, pos, entity): 6 | self.pos = vec2D(pos.x, pos.y) 7 | self.entity = entity 8 | self.x = self.pos.x * 32 9 | self.y = self.pos.y * 32 10 | 11 | def move(self): 12 | xPosFloat = self.entity.getPosIndexAsFloat().x 13 | if 10 < xPosFloat < 50: 14 | self.pos.x = -xPosFloat + 10 15 | self.x = self.pos.x * 32 16 | self.y = self.pos.y * 32 17 | -------------------------------------------------------------------------------- /classes/Collider.py: -------------------------------------------------------------------------------- 1 | class Collider: 2 | def __init__(self, entity, level): 3 | self.entity = entity 4 | self.level = level.level 5 | self.levelObj = level 6 | self.result = [] 7 | 8 | def checkX(self): 9 | if self.leftLevelBorderReached() or self.rightLevelBorderReached(): 10 | return 11 | try: 12 | rows = [ 13 | self.level[self.entity.getPosIndex().y], 14 | self.level[self.entity.getPosIndex().y + 1], 15 | ] 16 | except Exception: 17 | return 18 | for row in rows: 19 | tiles = row[self.entity.getPosIndex().x : self.entity.getPosIndex().x + 2] 20 | for tile in tiles: 21 | if tile.rect is not None: 22 | if self.entity.rect.colliderect(tile.rect): 23 | if self.entity.vel.x > 0: 24 | self.entity.rect.right = tile.rect.left 25 | self.entity.vel.x = 0 26 | if self.entity.vel.x < 0: 27 | self.entity.rect.left = tile.rect.right 28 | self.entity.vel.x = 0 29 | 30 | def checkY(self): 31 | self.entity.onGround = False 32 | 33 | try: 34 | rows = [ 35 | self.level[self.entity.getPosIndex().y], 36 | self.level[self.entity.getPosIndex().y + 1], 37 | ] 38 | except Exception: 39 | try: 40 | self.entity.gameOver() 41 | except Exception: 42 | self.entity.alive = None 43 | return 44 | for row in rows: 45 | tiles = row[self.entity.getPosIndex().x : self.entity.getPosIndex().x + 2] 46 | for tile in tiles: 47 | if tile.rect is not None: 48 | if self.entity.rect.colliderect(tile.rect): 49 | if self.entity.vel.y > 0: 50 | self.entity.onGround = True 51 | self.entity.rect.bottom = tile.rect.top 52 | self.entity.vel.y = 0 53 | # reset jump on bottom 54 | if self.entity.traits is not None: 55 | if "jumpTrait" in self.entity.traits: 56 | self.entity.traits["jumpTrait"].reset() 57 | if "bounceTrait" in self.entity.traits: 58 | self.entity.traits["bounceTrait"].reset() 59 | if self.entity.vel.y < 0: 60 | self.entity.rect.top = tile.rect.bottom 61 | self.entity.vel.y = 0 62 | 63 | def rightLevelBorderReached(self): 64 | if self.entity.getPosIndexAsFloat().x > self.levelObj.levelLength - 1: 65 | self.entity.rect.x = (self.levelObj.levelLength - 1) * 32 66 | self.entity.vel.x = 0 67 | return True 68 | 69 | def leftLevelBorderReached(self): 70 | if self.entity.rect.x < 0: 71 | self.entity.rect.x = 0 72 | self.entity.vel.x = 0 73 | return True 74 | -------------------------------------------------------------------------------- /classes/Dashboard.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | from classes.Font import Font 4 | 5 | 6 | class Dashboard(Font): 7 | def __init__(self, filePath, size, screen): 8 | Font.__init__(self, filePath, size) 9 | self.state = "menu" 10 | self.screen = screen 11 | self.levelName = "" 12 | self.points = 0 13 | self.coins = 0 14 | self.ticks = 0 15 | self.time = 0 16 | 17 | def update(self): 18 | self.drawText("MARIO", 50, 20, 15) 19 | self.drawText(self.pointString(), 50, 37, 15) 20 | 21 | self.drawText("@x{}".format(self.coinString()), 225, 37, 15) 22 | 23 | self.drawText("WORLD", 380, 20, 15) 24 | self.drawText(str(self.levelName), 395, 37, 15) 25 | 26 | self.drawText("TIME", 520, 20, 15) 27 | if self.state != "menu": 28 | self.drawText(self.timeString(), 535, 37, 15) 29 | 30 | # update Time 31 | self.ticks += 1 32 | if self.ticks == 60: 33 | self.ticks = 0 34 | self.time += 1 35 | 36 | def drawText(self, text, x, y, size): 37 | for char in text: 38 | charSprite = pygame.transform.scale(self.charSprites[char], (size, size)) 39 | self.screen.blit(charSprite, (x, y)) 40 | if char == " ": 41 | x += size//2 42 | else: 43 | x += size 44 | 45 | def coinString(self): 46 | return "{:02d}".format(self.coins) 47 | 48 | def pointString(self): 49 | return "{:06d}".format(self.points) 50 | 51 | def timeString(self): 52 | return "{:03d}".format(self.time) 53 | -------------------------------------------------------------------------------- /classes/EntityCollider.py: -------------------------------------------------------------------------------- 1 | class EntityCollider: 2 | def __init__(self, entity): 3 | self.entity = entity 4 | 5 | def check(self, target): 6 | if self.entity.rect.colliderect(target.rect): 7 | return self.determineSide(target.rect, self.entity.rect) 8 | return CollisionState(False, False) 9 | 10 | def determineSide(self, rect1, rect2): 11 | if ( 12 | rect1.collidepoint(rect2.bottomleft) 13 | or rect1.collidepoint(rect2.bottomright) 14 | or rect1.collidepoint(rect2.midbottom) 15 | ): 16 | if rect2.collidepoint( 17 | (rect1.midleft[0] / 2, rect1.midleft[1] / 2) 18 | ) or rect2.collidepoint((rect1.midright[0] / 2, rect1.midright[1] / 2)): 19 | return CollisionState(True, False) 20 | else: 21 | if self.entity.vel.y > 0: 22 | return CollisionState(True, True) 23 | return CollisionState(True, False) 24 | 25 | 26 | class CollisionState: 27 | def __init__(self, _isColliding, _isTop): 28 | self.isColliding = _isColliding 29 | self.isTop = _isTop 30 | -------------------------------------------------------------------------------- /classes/Font.py: -------------------------------------------------------------------------------- 1 | from classes.Spritesheet import Spritesheet 2 | import pygame 3 | 4 | 5 | class Font(Spritesheet): 6 | def __init__(self, filePath, size): 7 | Spritesheet.__init__(self, filename=filePath) 8 | self.chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" 9 | self.charSprites = self.loadFont() 10 | 11 | def loadFont(self): 12 | font = {} 13 | row = 0 14 | charAt = 0 15 | 16 | for char in self.chars: 17 | if charAt == 16: 18 | charAt = 0 19 | row += 1 20 | font.update( 21 | { 22 | char: self.image_at( 23 | charAt, 24 | row, 25 | 2, 26 | colorkey=pygame.color.Color(0, 0, 0), 27 | xTileSize = 8, 28 | yTileSize = 8 29 | ) 30 | } 31 | ) 32 | charAt += 1 33 | return font 34 | -------------------------------------------------------------------------------- /classes/GaussianBlur.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from scipy.ndimage.filters import * 3 | 4 | class GaussianBlur: 5 | def __init__(self,kernelsize=7): 6 | self.kernel_size = kernelsize 7 | 8 | def filter(self, srfc, xpos, ypos, width, height): 9 | nSrfc = pygame.Surface((width,height)) 10 | pxa = pygame.surfarray.array3d(srfc) 11 | blurred = gaussian_filter(pxa,sigma=(self.kernel_size, self.kernel_size, 0)) 12 | pygame.surfarray.blit_array(nSrfc, blurred) 13 | del pxa 14 | return nSrfc 15 | -------------------------------------------------------------------------------- /classes/Input.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | import sys 4 | 5 | 6 | class Input: 7 | def __init__(self, entity): 8 | self.mouseX = 0 9 | self.mouseY = 0 10 | self.entity = entity 11 | 12 | def checkForInput(self): 13 | events = pygame.event.get() 14 | self.checkForKeyboardInput() 15 | self.checkForMouseInput(events) 16 | self.checkForQuitAndRestartInputEvents(events) 17 | 18 | def checkForKeyboardInput(self): 19 | pressedKeys = pygame.key.get_pressed() 20 | 21 | if pressedKeys[K_LEFT] or pressedKeys[K_h] and not pressedKeys[K_RIGHT]: 22 | self.entity.traits["goTrait"].direction = -1 23 | elif pressedKeys[K_RIGHT] or pressedKeys[K_l] and not pressedKeys[K_LEFT]: 24 | self.entity.traits["goTrait"].direction = 1 25 | else: 26 | self.entity.traits['goTrait'].direction = 0 27 | 28 | isJumping = pressedKeys[K_SPACE] or pressedKeys[K_UP] or pressedKeys[K_k] 29 | self.entity.traits['jumpTrait'].jump(isJumping) 30 | 31 | self.entity.traits['goTrait'].boost = pressedKeys[K_LSHIFT] 32 | 33 | def checkForMouseInput(self,events): 34 | mouseX, mouseY = pygame.mouse.get_pos() 35 | if self.isRightMouseButtonPressed(events): 36 | self.entity.levelObj.addKoopa( 37 | mouseY / 32, mouseX / 32 - self.entity.camera.pos.x 38 | ) 39 | self.entity.levelObj.addGoomba( 40 | mouseY / 32, mouseX / 32 - self.entity.camera.pos.x 41 | ) 42 | if self.isLeftMouseButtonPressed(events): 43 | self.entity.levelObj.addCoin( 44 | mouseX / 32 - self.entity.camera.pos.x, mouseY / 32 45 | ) 46 | 47 | def checkForQuitAndRestartInputEvents(self,events): 48 | for event in events: 49 | if event.type == pygame.QUIT: 50 | pygame.quit() 51 | sys.exit() 52 | if event.type == pygame.KEYDOWN and \ 53 | (event.key == pygame.K_ESCAPE or event.key == pygame.K_F5): 54 | self.entity.pause = True 55 | self.entity.pauseObj.createBackgroundBlur() 56 | 57 | def isLeftMouseButtonPressed(self, events): 58 | return self.checkMouse(events,1) 59 | 60 | 61 | 62 | def isRightMouseButtonPressed(self, events): 63 | return self.checkMouse(events,3) 64 | 65 | 66 | def checkMouse(self, events, button): 67 | for e in events: 68 | if e.type == pygame.MOUSEBUTTONUP: 69 | if e.button == button: 70 | return True 71 | else: 72 | return False 73 | 74 | 75 | -------------------------------------------------------------------------------- /classes/Level.py: -------------------------------------------------------------------------------- 1 | import json 2 | import pygame 3 | 4 | from classes.Sprites import Sprites 5 | from classes.Tile import Tile 6 | from entities.Coin import Coin 7 | from entities.Goomba import Goomba 8 | from entities.Koopa import Koopa 9 | from entities.RandomBox import RandomBox 10 | 11 | class Level: 12 | def __init__(self, screen, sound, dashboard): 13 | self.sprites = Sprites() 14 | self.dashboard = dashboard 15 | self.sound = sound 16 | self.screen = screen 17 | self.level = None 18 | self.levelLength = 0 19 | self.entityList = [] 20 | 21 | def loadLevel(self, levelname): 22 | with open("./levels/{}.json".format(levelname)) as jsonData: 23 | data = json.load(jsonData) 24 | self.loadLayers(data) 25 | self.loadObjects(data) 26 | self.loadEntities(data) 27 | self.levelLength = data["length"] 28 | 29 | def loadEntities(self, data): 30 | try: 31 | [self.addRandomBox(x, y) for x, y in data["level"]["entities"]["randomBox"]] 32 | [self.addGoomba(x, y) for x, y in data["level"]["entities"]["Goomba"]] 33 | [self.addKoopa(x, y) for x, y in data["level"]["entities"]["Koopa"]] 34 | [self.addCoin(x, y) for x, y in data["level"]["entities"]["coin"]] 35 | except: 36 | #if no entities in Level 37 | pass 38 | 39 | def loadLayers(self, data): 40 | layers = [] 41 | for x in range(*data["level"]["layers"]["sky"]["x"]): 42 | layers.append( 43 | ( 44 | [ 45 | Tile(self.sprites.spriteCollection.get("sky"), None) 46 | for y in range(*data["level"]["layers"]["sky"]["y"]) 47 | ] 48 | + [ 49 | Tile( 50 | self.sprites.spriteCollection.get("ground"), 51 | pygame.Rect(x * 32, (y - 1) * 32, 32, 32), 52 | ) 53 | for y in range(*data["level"]["layers"]["ground"]["y"]) 54 | ] 55 | ) 56 | ) 57 | self.level = list(map(list, zip(*layers))) 58 | 59 | def loadObjects(self, data): 60 | for x, y in data["level"]["objects"]["bush"]: 61 | self.addBushSprite(x, y) 62 | for x, y in data["level"]["objects"]["cloud"]: 63 | self.addCloudSprite(x, y) 64 | for x, y, z in data["level"]["objects"]["pipe"]: 65 | self.addPipeSprite(x, y, z) 66 | for x, y in data["level"]["objects"]["sky"]: 67 | self.level[y][x] = Tile(self.sprites.spriteCollection.get("sky"), None) 68 | for x, y in data["level"]["objects"]["ground"]: 69 | self.level[y][x] = Tile( 70 | self.sprites.spriteCollection.get("ground"), 71 | pygame.Rect(x * 32, y * 32, 32, 32), 72 | ) 73 | 74 | def updateEntities(self, cam): 75 | for entity in self.entityList: 76 | entity.update(cam) 77 | if entity.alive is None: 78 | self.entityList.remove(entity) 79 | 80 | def drawLevel(self, camera): 81 | try: 82 | for y in range(0, 15): 83 | for x in range(0 - int(camera.pos.x + 1), 20 - int(camera.pos.x - 1)): 84 | if self.level[y][x].sprite is not None: 85 | if self.level[y][x].sprite.redrawBackground: 86 | self.screen.blit( 87 | self.sprites.spriteCollection.get("sky").image, 88 | ((x + camera.pos.x) * 32, y * 32), 89 | ) 90 | self.level[y][x].sprite.drawSprite( 91 | x + camera.pos.x, y, self.screen 92 | ) 93 | self.updateEntities(camera) 94 | except IndexError: 95 | return 96 | 97 | def addCloudSprite(self, x, y): 98 | try: 99 | for yOff in range(0, 2): 100 | for xOff in range(0, 3): 101 | self.level[y + yOff][x + xOff] = Tile( 102 | self.sprites.spriteCollection.get( 103 | "cloud{}_{}".format(yOff + 1, xOff + 1) 104 | ), 105 | None, 106 | ) 107 | except IndexError: 108 | return 109 | 110 | def addPipeSprite(self, x, y, length=2): 111 | try: 112 | # add Pipe Head 113 | self.level[y][x] = Tile( 114 | self.sprites.spriteCollection.get("pipeL"), 115 | pygame.Rect(x * 32, y * 32, 32, 32), 116 | ) 117 | self.level[y][x + 1] = Tile( 118 | self.sprites.spriteCollection.get("pipeR"), 119 | pygame.Rect((x + 1) * 32, y * 32, 32, 32), 120 | ) 121 | # add pipe Body 122 | for i in range(1, length + 20): 123 | self.level[y + i][x] = Tile( 124 | self.sprites.spriteCollection.get("pipe2L"), 125 | pygame.Rect(x * 32, (y + i) * 32, 32, 32), 126 | ) 127 | self.level[y + i][x + 1] = Tile( 128 | self.sprites.spriteCollection.get("pipe2R"), 129 | pygame.Rect((x + 1) * 32, (y + i) * 32, 32, 32), 130 | ) 131 | except IndexError: 132 | return 133 | 134 | def addBushSprite(self, x, y): 135 | try: 136 | self.level[y][x] = Tile(self.sprites.spriteCollection.get("bush_1"), None) 137 | self.level[y][x + 1] = Tile( 138 | self.sprites.spriteCollection.get("bush_2"), None 139 | ) 140 | self.level[y][x + 2] = Tile( 141 | self.sprites.spriteCollection.get("bush_3"), None 142 | ) 143 | except IndexError: 144 | return 145 | 146 | def addRandomBox(self, x, y): 147 | self.level[y][x] = Tile(None, pygame.Rect(x * 32, y * 32 - 1, 32, 32)) 148 | self.entityList.append( 149 | RandomBox( 150 | self.screen, 151 | self.sprites.spriteCollection, 152 | x, 153 | y, 154 | self.sound, 155 | self.dashboard, 156 | ) 157 | ) 158 | 159 | def addCoin(self, x, y): 160 | self.entityList.append(Coin(self.screen, self.sprites.spriteCollection, x, y)) 161 | 162 | def addGoomba(self, x, y): 163 | self.entityList.append( 164 | Goomba(self.screen, self.sprites.spriteCollection, x, y, self) 165 | ) 166 | 167 | def addKoopa(self, x, y): 168 | self.entityList.append( 169 | Koopa(self.screen, self.sprites.spriteCollection, x, y, self) 170 | ) 171 | -------------------------------------------------------------------------------- /classes/Maths.py: -------------------------------------------------------------------------------- 1 | class vec2D: 2 | def __init__(self, x=0, y=0): 3 | self.x = x 4 | self.y = y 5 | -------------------------------------------------------------------------------- /classes/Menu.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sys 3 | import os 4 | import pygame 5 | 6 | from classes.Spritesheet import Spritesheet 7 | 8 | 9 | class Menu: 10 | def __init__(self, screen, dashboard, level, sound): 11 | self.screen = screen 12 | self.sound = sound 13 | self.start = False 14 | self.inSettings = False 15 | self.state = 0 16 | self.level = level 17 | self.music = True 18 | self.sfx = True 19 | self.currSelectedLevel = 1 20 | self.levelNames = [] 21 | self.inChoosingLevel = False 22 | self.dashboard = dashboard 23 | self.levelCount = 0 24 | self.spritesheet = Spritesheet("./img/title_screen.png") 25 | self.menu_banner = self.spritesheet.image_at( 26 | 0, 27 | 60, 28 | 2, 29 | colorkey=[255, 0, 220], 30 | ignoreTileSize=True, 31 | xTileSize=180, 32 | yTileSize=88, 33 | ) 34 | self.menu_dot = self.spritesheet.image_at( 35 | 0, 150, 2, colorkey=[255, 0, 220], ignoreTileSize=True 36 | ) 37 | self.menu_dot2 = self.spritesheet.image_at( 38 | 20, 150, 2, colorkey=[255, 0, 220], ignoreTileSize=True 39 | ) 40 | self.loadSettings("./settings.json") 41 | 42 | def update(self): 43 | self.checkInput() 44 | if self.inChoosingLevel: 45 | return 46 | 47 | self.drawMenuBackground() 48 | self.dashboard.update() 49 | 50 | if not self.inSettings: 51 | self.drawMenu() 52 | else: 53 | self.drawSettings() 54 | 55 | def drawDot(self): 56 | if self.state == 0: 57 | self.screen.blit(self.menu_dot, (145, 273)) 58 | self.screen.blit(self.menu_dot2, (145, 313)) 59 | self.screen.blit(self.menu_dot2, (145, 353)) 60 | elif self.state == 1: 61 | self.screen.blit(self.menu_dot, (145, 313)) 62 | self.screen.blit(self.menu_dot2, (145, 273)) 63 | self.screen.blit(self.menu_dot2, (145, 353)) 64 | elif self.state == 2: 65 | self.screen.blit(self.menu_dot, (145, 353)) 66 | self.screen.blit(self.menu_dot2, (145, 273)) 67 | self.screen.blit(self.menu_dot2, (145, 313)) 68 | 69 | def loadSettings(self, url): 70 | try: 71 | with open(url) as jsonData: 72 | data = json.load(jsonData) 73 | if data["sound"]: 74 | self.music = True 75 | self.sound.music_channel.play(self.sound.soundtrack) 76 | else: 77 | self.music = False 78 | if data["sfx"]: 79 | self.sfx = True 80 | self.sound.allowSFX = True 81 | else: 82 | self.sound.allowSFX = False 83 | self.sfx = False 84 | except (IOError, OSError): 85 | self.music = False 86 | self.sound.allowSFX = False 87 | self.sfx = False 88 | self.saveSettings("./settings.json") 89 | 90 | def saveSettings(self, url): 91 | data = {"sound": self.music, "sfx": self.sfx} 92 | with open(url, "w") as outfile: 93 | json.dump(data, outfile) 94 | 95 | def drawMenu(self): 96 | self.drawDot() 97 | self.dashboard.drawText("CHOOSE LEVEL", 180, 280, 24) 98 | self.dashboard.drawText("SETTINGS", 180, 320, 24) 99 | self.dashboard.drawText("EXIT", 180, 360, 24) 100 | 101 | 102 | def drawMenuBackground(self,withBanner=True): 103 | for y in range(0, 13): 104 | for x in range(0, 20): 105 | self.screen.blit( 106 | self.level.sprites.spriteCollection.get("sky").image, 107 | (x * 32, y * 32), 108 | ) 109 | for y in range(13, 15): 110 | for x in range(0, 20): 111 | self.screen.blit( 112 | self.level.sprites.spriteCollection.get("ground").image, 113 | (x * 32, y * 32), 114 | ) 115 | if(withBanner): 116 | self.screen.blit(self.menu_banner, (150, 80)) 117 | self.screen.blit( 118 | self.level.sprites.spriteCollection.get("mario_idle").image, 119 | (2 * 32, 12 * 32), 120 | ) 121 | self.screen.blit( 122 | self.level.sprites.spriteCollection.get("bush_1").image, (14 * 32, 12 * 32) 123 | ) 124 | self.screen.blit( 125 | self.level.sprites.spriteCollection.get("bush_2").image, (15 * 32, 12 * 32) 126 | ) 127 | self.screen.blit( 128 | self.level.sprites.spriteCollection.get("bush_2").image, (16 * 32, 12 * 32) 129 | ) 130 | self.screen.blit( 131 | self.level.sprites.spriteCollection.get("bush_2").image, (17 * 32, 12 * 32) 132 | ) 133 | self.screen.blit( 134 | self.level.sprites.spriteCollection.get("bush_3").image, (18 * 32, 12 * 32) 135 | ) 136 | self.screen.blit(self.level.sprites.spriteCollection.get("goomba-1").image,(18.5*32,12*32)) 137 | 138 | def drawSettings(self): 139 | self.drawDot() 140 | self.dashboard.drawText("MUSIC", 180, 280, 24) 141 | if self.music: 142 | self.dashboard.drawText("ON", 340, 280, 24) 143 | else: 144 | self.dashboard.drawText("OFF", 340, 280, 24) 145 | self.dashboard.drawText("SFX", 180, 320, 24) 146 | if self.sfx: 147 | self.dashboard.drawText("ON", 340, 320, 24) 148 | else: 149 | self.dashboard.drawText("OFF", 340, 320, 24) 150 | self.dashboard.drawText("BACK", 180, 360, 24) 151 | 152 | def chooseLevel(self): 153 | self.drawMenuBackground(False) 154 | self.inChoosingLevel = True 155 | self.levelNames = self.loadLevelNames() 156 | self.drawLevelChooser() 157 | 158 | def drawBorder(self,x,y,width,height,color,thickness): 159 | pygame.draw.rect(self.screen,color,(x,y,width,thickness)) 160 | pygame.draw.rect(self.screen,color,(x,y+width,width,thickness)) 161 | pygame.draw.rect(self.screen,color,(x,y,thickness,width)) 162 | pygame.draw.rect(self.screen,color,(x+width,y,thickness,width+thickness)) 163 | 164 | def drawLevelChooser(self): 165 | j = 0 166 | offset = 75 167 | textOffset = 90 168 | for i, levelName in enumerate(self.loadLevelNames()): 169 | if self.currSelectedLevel == i+1: 170 | color = (255,255,255) 171 | else: 172 | color = (150,150,150) 173 | if i < 3: 174 | self.dashboard.drawText(levelName,175*i+textOffset,100,12) 175 | self.drawBorder(175*i+offset,55,125,75,color,5) 176 | else: 177 | self.dashboard.drawText(levelName,175*j+textOffset,250,12) 178 | self.drawBorder(175*j+offset,210,125,75,color,5) 179 | j+=1 180 | 181 | def loadLevelNames(self): 182 | files = [] 183 | res = [] 184 | for r, d, f in os.walk("./levels"): 185 | for file in f: 186 | files.append(os.path.join(r, file)) 187 | for f in files: 188 | res.append(os.path.split(f)[1].split(".")[0]) 189 | self.levelCount = len(res) 190 | return res 191 | 192 | def checkInput(self): 193 | events = pygame.event.get() 194 | for event in events: 195 | if event.type == pygame.QUIT: 196 | pygame.quit() 197 | sys.exit() 198 | if event.type == pygame.KEYDOWN: 199 | if event.key == pygame.K_ESCAPE: 200 | if self.inChoosingLevel or self.inSettings: 201 | self.inChoosingLevel = False 202 | self.inSettings = False 203 | self.__init__(self.screen, self.dashboard, self.level, self.sound) 204 | else: 205 | pygame.quit() 206 | sys.exit() 207 | elif event.key == pygame.K_UP or event.key == pygame.K_k: 208 | if self.inChoosingLevel: 209 | if self.currSelectedLevel > 3: 210 | self.currSelectedLevel -= 3 211 | self.drawLevelChooser() 212 | if self.state > 0: 213 | self.state -= 1 214 | elif event.key == pygame.K_DOWN or event.key == pygame.K_j: 215 | if self.inChoosingLevel: 216 | if self.currSelectedLevel+3 <= self.levelCount: 217 | self.currSelectedLevel += 3 218 | self.drawLevelChooser() 219 | if self.state < 2: 220 | self.state += 1 221 | elif event.key == pygame.K_LEFT or event.key == pygame.K_h: 222 | if self.currSelectedLevel > 1: 223 | self.currSelectedLevel -= 1 224 | self.drawLevelChooser() 225 | elif event.key == pygame.K_RIGHT or event.key == pygame.K_l: 226 | if self.currSelectedLevel < self.levelCount: 227 | self.currSelectedLevel += 1 228 | self.drawLevelChooser() 229 | elif event.key == pygame.K_RETURN: 230 | if self.inChoosingLevel: 231 | self.inChoosingLevel = False 232 | self.dashboard.state = "start" 233 | self.dashboard.time = 0 234 | self.level.loadLevel(self.levelNames[self.currSelectedLevel-1]) 235 | self.dashboard.levelName = self.levelNames[self.currSelectedLevel-1].split("Level")[1] 236 | self.start = True 237 | return 238 | if not self.inSettings: 239 | if self.state == 0: 240 | self.chooseLevel() 241 | elif self.state == 1: 242 | self.inSettings = True 243 | self.state = 0 244 | elif self.state == 2: 245 | pygame.quit() 246 | sys.exit() 247 | else: 248 | if self.state == 0: 249 | if self.music: 250 | self.sound.music_channel.stop() 251 | self.music = False 252 | else: 253 | self.sound.music_channel.play(self.sound.soundtrack) 254 | self.music = True 255 | self.saveSettings("./settings.json") 256 | elif self.state == 1: 257 | if self.sfx: 258 | self.sound.allowSFX = False 259 | self.sfx = False 260 | else: 261 | self.sound.allowSFX = True 262 | self.sfx = True 263 | self.saveSettings("./settings.json") 264 | elif self.state == 2: 265 | self.inSettings = False 266 | pygame.display.update() 267 | -------------------------------------------------------------------------------- /classes/Pause.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import sys 3 | 4 | from classes.Spritesheet import Spritesheet 5 | from classes.GaussianBlur import GaussianBlur 6 | 7 | class Pause: 8 | def __init__(self, screen, entity, dashboard): 9 | self.screen = screen 10 | self.entity = entity 11 | self.dashboard = dashboard 12 | self.state = 0 13 | self.spritesheet = Spritesheet("./img/title_screen.png") 14 | self.pause_srfc = GaussianBlur().filter(self.screen, 0, 0, 640, 480) 15 | self.dot = self.spritesheet.image_at( 16 | 0, 150, 2, colorkey=[255, 0, 220], ignoreTileSize=True 17 | ) 18 | self.gray_dot = self.spritesheet.image_at( 19 | 20, 150, 2, colorkey=[255, 0, 220], ignoreTileSize=True 20 | ) 21 | 22 | def update(self): 23 | self.screen.blit(self.pause_srfc,(0,0)) 24 | self.dashboard.drawText("PAUSED", 120, 160, 68) 25 | self.dashboard.drawText("CONTINUE", 150, 280, 32) 26 | self.dashboard.drawText("BACK TO MENU", 150, 320, 32) 27 | self.drawDot() 28 | pygame.display.update() 29 | self.checkInput() 30 | 31 | def drawDot(self): 32 | if self.state == 0: 33 | self.screen.blit(self.dot, (100, 275)) 34 | self.screen.blit(self.gray_dot, (100, 315)) 35 | elif self.state == 1: 36 | self.screen.blit(self.dot, (100, 315)) 37 | self.screen.blit(self.gray_dot, (100, 275)) 38 | 39 | def checkInput(self): 40 | events = pygame.event.get() 41 | for event in events: 42 | if event.type == pygame.QUIT: 43 | pygame.quit() 44 | sys.exit() 45 | if event.type == pygame.KEYDOWN: 46 | if event.key == pygame.K_RETURN: 47 | if self.state == 0: 48 | self.entity.pause = False 49 | elif self.state == 1: 50 | self.entity.restart = True 51 | elif event.key == pygame.K_UP: 52 | if self.state > 0: 53 | self.state -= 1 54 | elif event.key == pygame.K_DOWN: 55 | if self.state < 1: 56 | self.state += 1 57 | 58 | def createBackgroundBlur(self): 59 | self.pause_srfc = GaussianBlur().filter(self.screen, 0, 0, 640, 480) 60 | -------------------------------------------------------------------------------- /classes/Sound.py: -------------------------------------------------------------------------------- 1 | from pygame import mixer 2 | 3 | 4 | class Sound: 5 | def __init__(self): 6 | self.music_channel = mixer.Channel(0) 7 | self.music_channel.set_volume(0.2) 8 | self.sfx_channel = mixer.Channel(1) 9 | self.sfx_channel.set_volume(0.2) 10 | 11 | self.allowSFX = True 12 | 13 | self.soundtrack = mixer.Sound("./sfx/main_theme.ogg") 14 | self.coin = mixer.Sound("./sfx/coin.ogg") 15 | self.bump = mixer.Sound("./sfx/bump.ogg") 16 | self.stomp = mixer.Sound("./sfx/stomp.ogg") 17 | self.jump = mixer.Sound("./sfx/small_jump.ogg") 18 | self.death = mixer.Sound("./sfx/death.wav") 19 | 20 | def play_sfx(self, sfx): 21 | if self.allowSFX: 22 | self.sfx_channel.play(sfx) 23 | 24 | def play_music(self, music): 25 | self.music_channel.play(music) 26 | -------------------------------------------------------------------------------- /classes/Sprite.py: -------------------------------------------------------------------------------- 1 | class Sprite: 2 | def __init__(self, image, colliding, animation=None, redrawBackground=False): 3 | self.image = image 4 | self.colliding = colliding 5 | self.animation = animation 6 | self.redrawBackground = redrawBackground 7 | 8 | def drawSprite(self, x, y, screen): 9 | dimensions = (x * 32, y * 32) 10 | if self.animation is None: 11 | screen.blit(self.image, dimensions) 12 | else: 13 | self.animation.update() 14 | screen.blit(self.animation.image, dimensions) 15 | -------------------------------------------------------------------------------- /classes/Sprites.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from classes.Animation import Animation 4 | from classes.Sprite import Sprite 5 | from classes.Spritesheet import Spritesheet 6 | 7 | 8 | class Sprites: 9 | def __init__(self): 10 | self.spriteCollection = self.loadSprites( 11 | [ 12 | "./sprites/Mario.json", 13 | "./sprites/Goomba.json", 14 | "./sprites/Koopa.json", 15 | "./sprites/Animations.json", 16 | "./sprites/BackgroundSprites.json", 17 | "./sprites/ItemAnimations.json", 18 | ] 19 | ) 20 | 21 | def loadSprites(self, urlList): 22 | resDict = {} 23 | for url in urlList: 24 | with open(url) as jsonData: 25 | data = json.load(jsonData) 26 | mySpritesheet = Spritesheet(data["spriteSheetURL"]) 27 | dic = {} 28 | if data["type"] == "background": 29 | for sprite in data["sprites"]: 30 | try: 31 | colorkey = sprite["colorKey"] 32 | except KeyError: 33 | colorkey = None 34 | dic[sprite["name"]] = Sprite( 35 | mySpritesheet.image_at( 36 | sprite["x"], 37 | sprite["y"], 38 | sprite["scalefactor"], 39 | colorkey, 40 | ), 41 | sprite["collision"], 42 | None, 43 | sprite["redrawBg"], 44 | ) 45 | resDict.update(dic) 46 | continue 47 | elif data["type"] == "animation": 48 | for sprite in data["sprites"]: 49 | images = [] 50 | for image in sprite["images"]: 51 | images.append( 52 | mySpritesheet.image_at( 53 | image["x"], 54 | image["y"], 55 | image["scale"], 56 | colorkey=sprite["colorKey"], 57 | ) 58 | ) 59 | dic[sprite["name"]] = Sprite( 60 | None, 61 | None, 62 | animation=Animation(images, deltaTime=sprite["deltaTime"]), 63 | ) 64 | resDict.update(dic) 65 | continue 66 | elif data["type"] == "character" or data["type"] == "item": 67 | for sprite in data["sprites"]: 68 | try: 69 | colorkey = sprite["colorKey"] 70 | except KeyError: 71 | colorkey = None 72 | dic[sprite["name"]] = Sprite( 73 | mySpritesheet.image_at( 74 | sprite["x"], 75 | sprite["y"], 76 | sprite["scalefactor"], 77 | colorkey, 78 | True, 79 | xTileSize=data["size"][0], 80 | yTileSize=data["size"][1], 81 | ), 82 | sprite["collision"], 83 | ) 84 | resDict.update(dic) 85 | continue 86 | return resDict 87 | -------------------------------------------------------------------------------- /classes/Spritesheet.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | class Spritesheet(object): 4 | def __init__(self, filename): 5 | try: 6 | self.sheet = pygame.image.load(filename) 7 | if self.sheet.get_alpha(): 8 | self.sheet = self.sheet.convert_alpha() 9 | else: 10 | self.sheet = self.sheet.convert() 11 | self.sheet.set_colorkey((0, 0, 0)) 12 | except pygame.error: 13 | print("Unable to load spritesheet image:", filename) 14 | raise SystemExit 15 | 16 | def image_at(self, x, y, scalingfactor, colorkey=None, ignoreTileSize=False, 17 | xTileSize=16, yTileSize=16): 18 | if ignoreTileSize: 19 | rect = pygame.Rect((x, y, xTileSize, yTileSize)) 20 | else: 21 | rect = pygame.Rect((x * xTileSize, y * yTileSize, xTileSize, yTileSize)) 22 | image = pygame.Surface(rect.size) 23 | image.blit(self.sheet, (0, 0), rect) 24 | if colorkey is not None: 25 | if colorkey is -1: 26 | colorkey = image.get_at((0, 0)) 27 | image.set_colorkey(colorkey, pygame.RLEACCEL) 28 | return pygame.transform.scale( 29 | image, (xTileSize * scalingfactor, yTileSize * scalingfactor) 30 | ) 31 | -------------------------------------------------------------------------------- /classes/Tile.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | 4 | class Tile: 5 | def __init__(self, sprite, rect): 6 | self.sprite = sprite 7 | self.rect = rect 8 | 9 | def drawRect(self, screen): 10 | try: 11 | pygame.draw.rect(screen, pygame.Color(255, 0, 0), self.rect, 1) 12 | except Exception: 13 | pass 14 | -------------------------------------------------------------------------------- /classes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/classes/__init__.py -------------------------------------------------------------------------------- /compile.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | import py2exe, glob 4 | 5 | setup( 6 | # this is the file that is run when you start the game from the command line. 7 | console=["main.py"], 8 | # data files - these are the non-python files, like images and sounds 9 | data_files=[ 10 | ("sprites", glob.glob("sprites\\*.json")), 11 | ("sfx", glob.glob("sfx\\*.ogg") + glob.glob("sfx\\*.wav")), 12 | ("levels", glob.glob("levels\\*.json")), 13 | ("img", glob.glob("img\\*.gif") + glob.glob("img\\*.png")), 14 | ("", ["settings.json"]), 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /entities/Coin.py: -------------------------------------------------------------------------------- 1 | from copy import copy 2 | 3 | from entities.EntityBase import EntityBase 4 | 5 | 6 | class Coin(EntityBase): 7 | def __init__(self, screen, spriteCollection, x, y, gravity=0): 8 | super(Coin, self).__init__(x, y, gravity) 9 | self.screen = screen 10 | self.spriteCollection = spriteCollection 11 | self.animation = copy(self.spriteCollection.get("coin").animation) 12 | self.type = "Item" 13 | 14 | def update(self, cam): 15 | if self.alive: 16 | self.animation.update() 17 | self.screen.blit(self.animation.image, (self.rect.x + cam.x, self.rect.y)) 18 | -------------------------------------------------------------------------------- /entities/EntityBase.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | from classes.Maths import vec2D 4 | 5 | 6 | class EntityBase(object): 7 | def __init__(self, x, y, gravity): 8 | self.vel = vec2D() 9 | self.rect = pygame.Rect(x * 32, y * 32, 32, 32) 10 | self.gravity = gravity 11 | self.traits = None 12 | self.alive = True 13 | self.timeAfterDeath = 5 14 | self.timer = 0 15 | self.type = "" 16 | self.onGround = False 17 | self.obeygravity = True 18 | 19 | def applyGravity(self): 20 | if self.obeygravity: 21 | self.vel.y += self.gravity 22 | 23 | def updateTraits(self): 24 | for trait in self.traits.values(): 25 | try: 26 | trait.update() 27 | except AttributeError: 28 | pass 29 | 30 | def getPosIndex(self): 31 | return vec2D(int(self.rect.x / 32), int(self.rect.y / 32)) 32 | 33 | def getPosIndexAsFloat(self): 34 | return vec2D(self.rect.x / 32.0, self.rect.y / 32.0) 35 | -------------------------------------------------------------------------------- /entities/Goomba.py: -------------------------------------------------------------------------------- 1 | from classes.Animation import Animation 2 | from classes.Maths import vec2D 3 | from entities.EntityBase import EntityBase 4 | from traits.leftrightwalk import LeftRightWalkTrait 5 | 6 | 7 | class Goomba(EntityBase): 8 | def __init__(self, screen, spriteColl, x, y, level): 9 | super(Goomba, self).__init__(y, x - 1, 1.25) 10 | self.spriteCollection = spriteColl 11 | self.animation = Animation( 12 | [ 13 | self.spriteCollection.get("goomba-1").image, 14 | self.spriteCollection.get("goomba-2").image, 15 | ] 16 | ) 17 | self.screen = screen 18 | self.leftrightTrait = LeftRightWalkTrait(self, level) 19 | self.type = "Mob" 20 | self.dashboard = level.dashboard 21 | 22 | def update(self, camera): 23 | if self.alive: 24 | self.applyGravity() 25 | self.drawGoomba(camera) 26 | self.leftrightTrait.update() 27 | else: 28 | self.onDead(camera) 29 | 30 | def drawGoomba(self, camera): 31 | self.screen.blit(self.animation.image, (self.rect.x + camera.x, self.rect.y)) 32 | self.animation.update() 33 | 34 | def onDead(self, camera): 35 | if self.timer == 0: 36 | self.setPointsTextStartPosition(self.rect.x + 3, self.rect.y) 37 | if self.timer < self.timeAfterDeath: 38 | self.movePointsTextUpAndDraw(camera) 39 | self.drawFlatGoomba(camera) 40 | else: 41 | self.alive = None 42 | self.timer += 0.1 43 | 44 | def drawFlatGoomba(self, camera): 45 | self.screen.blit( 46 | self.spriteCollection.get("goomba-flat").image, 47 | (self.rect.x + camera.x, self.rect.y), 48 | ) 49 | 50 | def setPointsTextStartPosition(self, x, y): 51 | self.textPos = vec2D(x, y) 52 | 53 | def movePointsTextUpAndDraw(self, camera): 54 | self.textPos.y += -0.5 55 | self.dashboard.drawText("100", self.textPos.x + camera.x, self.textPos.y, 8) 56 | -------------------------------------------------------------------------------- /entities/Item.py: -------------------------------------------------------------------------------- 1 | from copy import copy 2 | 3 | from classes.Dashboard import Dashboard 4 | from classes.Maths import vec2D 5 | 6 | 7 | class Item(Dashboard): 8 | def __init__(self, collection, screen, x, y): 9 | super(Item, self).__init__("./img/font.png", 8, screen) 10 | self.ItemPos = vec2D(x, y) 11 | self.itemVel = vec2D(0, 0) 12 | self.screen = screen 13 | self.coin_animation = copy(collection.get("coin-item").animation) 14 | self.sound_played = False 15 | 16 | def spawnCoin(self, cam, sound, dashboard): 17 | if not self.sound_played: 18 | self.sound_played = True 19 | dashboard.points += 100 20 | sound.play_sfx(sound.coin) 21 | self.coin_animation.update() 22 | if self.coin_animation.timer < 45: 23 | if self.coin_animation.timer < 15: 24 | self.itemVel.y -= 0.5 25 | self.ItemPos.y += self.itemVel.y 26 | elif self.coin_animation.timer < 45: 27 | self.itemVel.y += 0.5 28 | self.ItemPos.y += self.itemVel.y 29 | self.screen.blit( 30 | self.coin_animation.image, (self.ItemPos.x + cam.x, self.ItemPos.y) 31 | ) 32 | elif self.coin_animation.timer < 80: 33 | self.itemVel.y = -0.75 34 | self.ItemPos.y += self.itemVel.y 35 | self.drawText("100", self.ItemPos.x + 3 + cam.x, self.ItemPos.y, 8) 36 | -------------------------------------------------------------------------------- /entities/Koopa.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | from classes.Animation import Animation 4 | from classes.Maths import vec2D 5 | from entities.EntityBase import EntityBase 6 | from traits.leftrightwalk import LeftRightWalkTrait 7 | 8 | 9 | class Koopa(EntityBase): 10 | def __init__(self, screen, spriteColl, x, y, level): 11 | super(Koopa, self).__init__(y - 1, x, 1.25) 12 | self.spriteCollection = spriteColl 13 | self.animation = Animation( 14 | [ 15 | self.spriteCollection.get("koopa-1").image, 16 | self.spriteCollection.get("koopa-2").image, 17 | ] 18 | ) 19 | self.screen = screen 20 | self.leftrightTrait = LeftRightWalkTrait(self, level) 21 | self.timer = 0 22 | self.timeAfterDeath = 35 23 | self.type = "Mob" 24 | self.dashboard = level.dashboard 25 | 26 | def update(self, camera): 27 | if self.alive == True: 28 | self.updateAlive(camera) 29 | elif self.alive == "sleeping": 30 | self.sleepingInShell(camera) 31 | elif self.alive == "shellBouncing": 32 | self.shellBouncing(camera) 33 | elif self.alive == False: 34 | self.die(camera) 35 | 36 | def drawKoopa(self, camera): 37 | if self.leftrightTrait.direction == -1: 38 | self.screen.blit( 39 | self.animation.image, (self.rect.x + camera.x, self.rect.y - 32) 40 | ) 41 | else: 42 | self.screen.blit( 43 | pygame.transform.flip(self.animation.image, True, False), 44 | (self.rect.x + camera.x, self.rect.y - 32), 45 | ) 46 | 47 | def shellBouncing(self, camera): 48 | self.leftrightTrait.speed = 4 49 | self.applyGravity() 50 | self.animation.image = self.spriteCollection.get("koopa-hiding").image 51 | self.drawKoopa(camera) 52 | self.leftrightTrait.update() 53 | 54 | def die(self, camera): 55 | if self.timer == 0: 56 | self.textPos = vec2D(self.rect.x + 3, self.rect.y - 32) 57 | if self.timer < self.timeAfterDeath: 58 | self.textPos.y += -0.5 59 | self.dashboard.drawText("100", self.textPos.x + camera.x, self.textPos.y, 8) 60 | self.vel.y -= 0.5 61 | self.rect.y += self.vel.y 62 | self.screen.blit( 63 | self.spriteCollection.get("koopa-hiding").image, 64 | (self.rect.x + camera.x, self.rect.y - 32), 65 | ) 66 | else: 67 | self.vel.y += 0.3 68 | self.rect.y += self.vel.y 69 | self.textPos.y += -0.5 70 | self.dashboard.drawText("100", self.textPos.x + camera.x, self.textPos.y, 8) 71 | self.screen.blit( 72 | self.spriteCollection.get("koopa-hiding").image, 73 | (self.rect.x + camera.x, self.rect.y - 32), 74 | ) 75 | if self.timer > 500: 76 | # delete entity 77 | self.alive = None 78 | self.timer += 6 79 | 80 | def sleepingInShell(self, camera): 81 | if self.timer < self.timeAfterDeath: 82 | self.screen.blit( 83 | self.spriteCollection.get("koopa-hiding").image, 84 | (self.rect.x + camera.x, self.rect.y - 32), 85 | ) 86 | else: 87 | self.alive = True 88 | self.timer = 0 89 | self.timer += 0.1 90 | 91 | def updateAlive(self, camera): 92 | self.applyGravity() 93 | self.drawKoopa(camera) 94 | self.animation.update() 95 | self.leftrightTrait.update() 96 | -------------------------------------------------------------------------------- /entities/Mario.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | from classes.Animation import Animation 4 | from classes.Camera import Camera 5 | from classes.Collider import Collider 6 | from classes.EntityCollider import EntityCollider 7 | from classes.Input import Input 8 | from classes.Sprites import Sprites 9 | from entities.EntityBase import EntityBase 10 | from traits.bounce import bounceTrait 11 | from traits.go import goTrait 12 | from traits.jump import jumpTrait 13 | from classes.Pause import Pause 14 | 15 | 16 | class Mario(EntityBase): 17 | def __init__(self, x, y, level, screen, dashboard, sound, gravity=0.75): 18 | super(Mario, self).__init__(x, y, gravity) 19 | self.spriteCollection = Sprites().spriteCollection 20 | self.camera = Camera(self.rect, self) 21 | self.sound = sound 22 | self.input = Input(self) 23 | self.inAir = False 24 | self.inJump = False 25 | self.animation = Animation( 26 | [ 27 | self.spriteCollection["mario_run1"].image, 28 | self.spriteCollection["mario_run2"].image, 29 | self.spriteCollection["mario_run3"].image, 30 | ], 31 | self.spriteCollection["mario_idle"].image, 32 | self.spriteCollection["mario_jump"].image, 33 | ) 34 | 35 | self.traits = { 36 | "jumpTrait": jumpTrait(self), 37 | "goTrait": goTrait(self.animation, screen, self.camera, self), 38 | "bounceTrait": bounceTrait(self), 39 | } 40 | 41 | self.levelObj = level 42 | self.collision = Collider(self, level) 43 | self.screen = screen 44 | self.EntityCollider = EntityCollider(self) 45 | self.dashboard = dashboard 46 | self.restart = False 47 | self.pause = False 48 | self.pauseObj = Pause(screen, self, dashboard) 49 | 50 | def update(self): 51 | self.updateTraits() 52 | self.moveMario() 53 | self.camera.move() 54 | self.applyGravity() 55 | self.checkEntityCollision() 56 | self.input.checkForInput() 57 | 58 | def moveMario(self): 59 | self.rect.y += self.vel.y 60 | self.collision.checkY() 61 | self.rect.x += self.vel.x 62 | self.collision.checkX() 63 | 64 | def checkEntityCollision(self): 65 | for ent in self.levelObj.entityList: 66 | collisionState = self.EntityCollider.check(ent) 67 | if collisionState.isColliding: 68 | if ent.type == "Item": 69 | self._onCollisionWithItem(ent) 70 | elif ent.type == "Block": 71 | self._onCollisionWithBlock(ent) 72 | elif ent.type == "Mob": 73 | self._onCollisionWithMob(ent, collisionState) 74 | 75 | def _onCollisionWithItem(self, item): 76 | self.levelObj.entityList.remove(item) 77 | self.dashboard.points += 100 78 | self.dashboard.coins += 1 79 | self.sound.play_sfx(self.sound.coin) 80 | 81 | def _onCollisionWithBlock(self, block): 82 | if not block.triggered: 83 | self.dashboard.coins += 1 84 | self.sound.play_sfx(self.sound.bump) 85 | block.triggered = True 86 | 87 | def _onCollisionWithMob(self, mob, collisionState): 88 | if collisionState.isTop and (mob.alive or mob.alive == "shellBouncing"): 89 | self.sound.play_sfx(self.sound.stomp) 90 | self.rect.bottom = mob.rect.top 91 | self.bounce() 92 | self.killEntity(mob) 93 | elif collisionState.isTop and mob.alive == "sleeping": 94 | self.sound.play_sfx(self.sound.stomp) 95 | self.rect.bottom = mob.rect.top 96 | mob.timer = 0 97 | self.bounce() 98 | mob.alive = False 99 | elif collisionState.isColliding and mob.alive == "sleeping": 100 | if mob.rect.x < self.rect.x: 101 | mob.leftrightTrait.direction = -1 102 | mob.rect.x += -5 103 | else: 104 | mob.rect.x += 5 105 | mob.leftrightTrait.direction = 1 106 | mob.alive = "shellBouncing" 107 | elif collisionState.isColliding and mob.alive: 108 | self.gameOver() 109 | 110 | def bounce(self): 111 | self.traits["bounceTrait"].jump = True 112 | 113 | def killEntity(self, ent): 114 | if ent.__class__.__name__ != "Koopa": 115 | ent.alive = False 116 | else: 117 | ent.timer = 0 118 | ent.alive = "sleeping" 119 | self.dashboard.points += 100 120 | 121 | def gameOver(self): 122 | srf = pygame.Surface((640, 480)) 123 | srf.set_colorkey((255, 255, 255), pygame.RLEACCEL) 124 | srf.set_alpha(128) 125 | self.sound.music_channel.stop() 126 | self.sound.music_channel.play(self.sound.death) 127 | 128 | for i in range(500, 20, -2): 129 | srf.fill((0, 0, 0)) 130 | pygame.draw.circle( 131 | srf, 132 | (255, 255, 255), 133 | (int(self.camera.x + self.rect.x) + 16, self.rect.y + 16), 134 | i, 135 | ) 136 | self.screen.blit(srf, (0, 0)) 137 | pygame.display.update() 138 | self.input.checkForInput() 139 | while self.sound.music_channel.get_busy(): 140 | pygame.display.update() 141 | self.input.checkForInput() 142 | self.restart = True 143 | 144 | def getPos(self): 145 | return self.camera.x + self.rect.x, self.rect.y 146 | 147 | def setPos(self,x,y): 148 | self.rect.x = x 149 | self.rect.y = y 150 | -------------------------------------------------------------------------------- /entities/RandomBox.py: -------------------------------------------------------------------------------- 1 | from copy import copy 2 | 3 | from entities.EntityBase import EntityBase 4 | from entities.Item import Item 5 | 6 | 7 | class RandomBox(EntityBase): 8 | def __init__(self, screen, spriteCollection, x, y, sound, dashboard, gravity=0): 9 | super(RandomBox, self).__init__(x, y, gravity) 10 | self.screen = screen 11 | self.spriteCollection = spriteCollection 12 | self.animation = copy(self.spriteCollection.get("randomBox").animation) 13 | self.type = "Block" 14 | self.triggered = False 15 | self.time = 0 16 | self.maxTime = 10 17 | self.sound = sound 18 | self.dashboard = dashboard 19 | self.vel = 1 20 | self.item = Item(spriteCollection, screen, self.rect.x, self.rect.y) 21 | 22 | def update(self, cam): 23 | if self.alive and not self.triggered: 24 | self.animation.update() 25 | else: 26 | self.animation.image = self.spriteCollection.get("empty").image 27 | self.item.spawnCoin(cam, self.sound, self.dashboard) 28 | if self.time < self.maxTime: 29 | self.time += 1 30 | self.rect.y -= self.vel 31 | else: 32 | if self.time < self.maxTime * 2: 33 | self.time += 1 34 | self.rect.y += self.vel 35 | self.screen.blit( 36 | self.spriteCollection.get("sky").image, 37 | (self.rect.x + cam.x, self.rect.y + 2), 38 | ) 39 | self.screen.blit(self.animation.image, (self.rect.x + cam.x, self.rect.y - 1)) 40 | -------------------------------------------------------------------------------- /entities/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/entities/__init__.py -------------------------------------------------------------------------------- /img/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/1.PNG -------------------------------------------------------------------------------- /img/2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/2.PNG -------------------------------------------------------------------------------- /img/3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/3.PNG -------------------------------------------------------------------------------- /img/4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/4.PNG -------------------------------------------------------------------------------- /img/Items.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/Items.png -------------------------------------------------------------------------------- /img/characters.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/characters.gif -------------------------------------------------------------------------------- /img/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/font.png -------------------------------------------------------------------------------- /img/koopas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/koopas.png -------------------------------------------------------------------------------- /img/pics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/pics.png -------------------------------------------------------------------------------- /img/pictures.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/pictures.PNG -------------------------------------------------------------------------------- /img/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/tiles.png -------------------------------------------------------------------------------- /img/title_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/title_screen.png -------------------------------------------------------------------------------- /img/yoshis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/img/yoshis.png -------------------------------------------------------------------------------- /levels/Level1-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "id":0, 3 | "length":60, 4 | "level":{ 5 | "objects": { 6 | "bush":[ 7 | [2,12], 8 | [17,12], 9 | [24,12], 10 | [34,12], 11 | [40,12], 12 | [44,12] 13 | ], 14 | "sky":[ 15 | [48,13], 16 | [49,13], 17 | [48,14], 18 | [49,14] 19 | ], 20 | "cloud":[ 21 | [5,5], 22 | [13,3], 23 | [26,5], 24 | [32,3], 25 | [42,6], 26 | [55,4] 27 | ], 28 | "pipe":[ 29 | [8,10,4], 30 | [12,12,4], 31 | [22,12,4], 32 | [29,9,6] 33 | ], 34 | "ground":[ 35 | [40,9], 36 | [41,9], 37 | [42,9], 38 | [43,9], 39 | [44,9], 40 | [45,9], 41 | [45,9], 42 | [46,9], 43 | [47,9], 44 | [50,9], 45 | [51,9], 46 | [52,9], 47 | [53,9], 48 | [54,9], 49 | [55,9], 50 | [54,8], 51 | [55,8], 52 | [56,8], 53 | [57,8], 54 | [58,8], 55 | [59,8] 56 | ] 57 | }, 58 | "layers": { 59 | "sky":{ 60 | "x":[0,60], 61 | "y":[0,13] 62 | }, 63 | "ground":{ 64 | "x":[0,60], 65 | "y":[14,16] 66 | } 67 | }, 68 | "entities": { 69 | "randomBox":[ 70 | [4,8], 71 | [4,3], 72 | [42,5], 73 | [56,2] 74 | ], 75 | "coin":[ 76 | [21,8], 77 | [22,8], 78 | [23,8], 79 | [24,8], 80 | [21,7], 81 | [22,7], 82 | [23,7], 83 | [24,7], 84 | [59,9], 85 | [59,10], 86 | [59,11], 87 | [59,12], 88 | [58,9], 89 | [58,10], 90 | [58,11], 91 | [58,12], 92 | [57,9], 93 | [57,10], 94 | [57,11], 95 | [57,12], 96 | [56,9], 97 | [56,10], 98 | [56,11], 99 | [56,12] 100 | ], 101 | "Goomba":[ 102 | [12,12], 103 | [12,15], 104 | [10,8] 105 | ], 106 | "Koopa":[ 107 | [12,25], 108 | [12,22], 109 | [12,33], 110 | [12,40] 111 | ] 112 | } 113 | 114 | } 115 | } -------------------------------------------------------------------------------- /levels/Level1-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "id":1, 3 | "length":60, 4 | "level":{ 5 | "objects": { 6 | "bush":[ 7 | [2,12], 8 | [17,12], 9 | [24,12], 10 | [34,12], 11 | [40,12], 12 | [44,12] 13 | ], 14 | "sky":[ 15 | [48,13], 16 | [49,13], 17 | [48,14], 18 | [49,14] 19 | ], 20 | "cloud":[ 21 | [5,5], 22 | [13,3], 23 | [26,5], 24 | [32,3], 25 | [42,6], 26 | [55,4] 27 | ], 28 | "pipe":[ 29 | [8,10,4], 30 | [12,12,4], 31 | [22,12,4], 32 | [29,9,6] 33 | ], 34 | "ground":[ 35 | [40,9], 36 | [41,9], 37 | [42,9], 38 | [43,9], 39 | [44,9], 40 | [45,9], 41 | [45,9], 42 | [46,9], 43 | [47,9], 44 | [50,9], 45 | [51,9], 46 | [52,9], 47 | [53,9], 48 | [54,9], 49 | [55,9], 50 | [54,8], 51 | [55,8], 52 | [56,8], 53 | [57,8], 54 | [58,8], 55 | [59,8] 56 | ] 57 | }, 58 | "layers": { 59 | "sky":{ 60 | "x":[0,60], 61 | "y":[0,13] 62 | }, 63 | "ground":{ 64 | "x":[0,60], 65 | "y":[14,16] 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from classes.Dashboard import Dashboard 3 | from classes.Level import Level 4 | from classes.Menu import Menu 5 | from classes.Sound import Sound 6 | from entities.Mario import Mario 7 | 8 | 9 | windowSize = (640,480) 10 | def main(): 11 | pygame.mixer.pre_init(44100, -16, 2, 4096) 12 | pygame.init() 13 | screen = pygame.display.set_mode(windowSize) 14 | max_frame_rate = 60 15 | dashboard = Dashboard("./img/font.png", 8, screen) 16 | sound = Sound() 17 | level = Level(screen, sound, dashboard) 18 | menu = Menu(screen, dashboard, level, sound) 19 | 20 | while not menu.start: 21 | menu.update() 22 | 23 | mario = Mario(0, 0, level, screen, dashboard, sound) 24 | clock = pygame.time.Clock() 25 | 26 | while not mario.restart: 27 | pygame.display.set_caption("Super Mario running with {:d} FPS".format(int(clock.get_fps()))) 28 | if mario.pause: 29 | mario.pauseObj.update() 30 | else: 31 | level.drawLevel(mario.camera) 32 | dashboard.update() 33 | mario.update() 34 | pygame.display.update() 35 | clock.tick(max_frame_rate) 36 | main() 37 | 38 | 39 | if __name__ == "__main__": 40 | main() 41 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pygame==2.0.0.dev10 2 | scipy==1.4.1 3 | -------------------------------------------------------------------------------- /sfx/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/sfx/__init__.py -------------------------------------------------------------------------------- /sfx/bump.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/sfx/bump.ogg -------------------------------------------------------------------------------- /sfx/coin.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/sfx/coin.ogg -------------------------------------------------------------------------------- /sfx/death.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/sfx/death.wav -------------------------------------------------------------------------------- /sfx/kick.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/sfx/kick.ogg -------------------------------------------------------------------------------- /sfx/main_theme.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/sfx/main_theme.ogg -------------------------------------------------------------------------------- /sfx/powerup.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/sfx/powerup.ogg -------------------------------------------------------------------------------- /sfx/powerup_appears.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/sfx/powerup_appears.ogg -------------------------------------------------------------------------------- /sfx/small_jump.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/sfx/small_jump.ogg -------------------------------------------------------------------------------- /sfx/stomp.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/sfx/stomp.ogg -------------------------------------------------------------------------------- /sprites/Animations.json: -------------------------------------------------------------------------------- 1 | { 2 | "spriteSheetURL": "./img/tiles.png", 3 | "type":"animation", 4 | "sprites":[ 5 | { 6 | "name":"randomBox", 7 | "images":[ 8 | { 9 | "x":24, 10 | "y":0, 11 | "scale":2 12 | }, 13 | { 14 | "x":25, 15 | "y":0, 16 | "scale":2 17 | }, 18 | { 19 | "x":26, 20 | "y":0, 21 | "scale":2 22 | } 23 | ], 24 | "deltaTime":10, 25 | "colorKey": null 26 | }, 27 | { 28 | "name":"coin", 29 | "images":[ 30 | { 31 | "x":24, 32 | "y":1, 33 | "scale":2 34 | }, 35 | { 36 | "x":25, 37 | "y":1, 38 | "scale":2 39 | }, 40 | { 41 | "x":26, 42 | "y":1, 43 | "scale":2 44 | } 45 | ], 46 | "deltaTime":10, 47 | "colorKey": -1 48 | } 49 | ] 50 | } 51 | 52 | -------------------------------------------------------------------------------- /sprites/BackgroundSprites.json: -------------------------------------------------------------------------------- 1 | { 2 | "spriteSheetURL": "./img/tiles.png", 3 | "type":"background", 4 | "sprites":[ 5 | { 6 | "name":"sky", 7 | "x":3, 8 | "y":23, 9 | "redrawBg":false, 10 | "scalefactor":2, 11 | "collision":false 12 | }, 13 | { 14 | "name":"bricks", 15 | "x":1, 16 | "y":0, 17 | "redrawBg":false, 18 | "scalefactor":2, 19 | "collision":true 20 | }, 21 | { 22 | "name":"ground", 23 | "x":0, 24 | "y":0, 25 | "redrawBg":false, 26 | "scalefactor":2, 27 | "collision":true 28 | }, 29 | { 30 | "name":"empty", 31 | "x":27, 32 | "y":0, 33 | "redrawBg":false, 34 | "scalefactor":2, 35 | "collision":true 36 | }, 37 | { 38 | "name":"pipeL", 39 | "x":0, 40 | "y":10, 41 | "redrawBg":false, 42 | "scalefactor":2, 43 | "collision":true 44 | }, 45 | { 46 | "name":"pipeR", 47 | "x":1, 48 | "y":10, 49 | "scalefactor":2, 50 | "collision":true, 51 | "redrawBg":false, 52 | "colorKey": [0,0,0] 53 | }, 54 | { 55 | "name":"pipe2L", 56 | "x":0, 57 | "y":11, 58 | "redrawBg":true, 59 | "scalefactor":2, 60 | "collision":true, 61 | "colorKey": [0,0,0] 62 | }, 63 | { 64 | "name":"pipe2R", 65 | "x":1, 66 | "y":11, 67 | "redrawBg":true, 68 | "scalefactor":2, 69 | "collision":true, 70 | "colorKey": [0,0,0] 71 | }, 72 | { 73 | "name":"cloud1_1", 74 | "x":0, 75 | "y":20, 76 | "redrawBg":true, 77 | "scalefactor":2, 78 | "collision":false, 79 | "colorKey": [0,0,0] 80 | }, 81 | { 82 | "name":"cloud1_2", 83 | "x":1, 84 | "y":20, 85 | "redrawBg":true, 86 | "scalefactor":2, 87 | "collision":false, 88 | "colorKey": [0,0,0] 89 | }, 90 | { 91 | "name":"cloud1_3", 92 | "x":2, 93 | "y":20, 94 | "redrawBg":true, 95 | "scalefactor":2, 96 | "collision":false, 97 | "colorKey": [0,0,0] 98 | }, 99 | { 100 | "name":"cloud2_1", 101 | "x":0, 102 | "y":21, 103 | "redrawBg":true, 104 | "scalefactor":2, 105 | "collision":false, 106 | "colorKey": [0,0,0] 107 | }, 108 | { 109 | "name":"cloud2_2", 110 | "x":1, 111 | "y":21, 112 | "redrawBg":true, 113 | "scalefactor":2, 114 | "collision":false, 115 | "colorKey": [0,0,0] 116 | }, 117 | { 118 | "name":"cloud2_3", 119 | "x":2, 120 | "y":21, 121 | "redrawBg":true, 122 | "scalefactor":2, 123 | "collision":false, 124 | "colorKey": [0,0,0] 125 | }, 126 | { 127 | "name":"bush_1", 128 | "x":11, 129 | "y":11, 130 | "redrawBg":true, 131 | "scalefactor":2, 132 | "collision":false, 133 | "colorKey": [0,0,0] 134 | }, 135 | { 136 | "name":"bush_2", 137 | "x":12, 138 | "y":11, 139 | "redrawBg":true, 140 | "scalefactor":2, 141 | "collision":false, 142 | "colorKey": [0,0,0] 143 | }, 144 | { 145 | "name":"bush_3", 146 | "x":13, 147 | "y":11, 148 | "redrawBg":true, 149 | "scalefactor":2, 150 | "collision":false, 151 | "colorKey": [0,0,0] 152 | } 153 | ] 154 | } 155 | 156 | 157 | -------------------------------------------------------------------------------- /sprites/Goomba.json: -------------------------------------------------------------------------------- 1 | { 2 | "spriteSheetURL": "./img/characters.gif", 3 | "type":"character", 4 | "size":[16,16], 5 | "sprites":[ 6 | { 7 | "name":"goomba-1", 8 | "x":296, 9 | "y":187, 10 | "scalefactor":2, 11 | "colorKey":-1, 12 | "collision":false 13 | }, 14 | { 15 | "name":"goomba-2", 16 | "x":315, 17 | "y":187, 18 | "scalefactor":2, 19 | "colorKey":-1, 20 | "collision":false 21 | }, 22 | { 23 | "name":"goomba-flat", 24 | "x":277, 25 | "y":187, 26 | "scalefactor":2, 27 | "colorKey":-1, 28 | "collision":false 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /sprites/ItemAnimations.json: -------------------------------------------------------------------------------- 1 | { 2 | "spriteSheetURL": "./img/Items.png", 3 | "type":"animation", 4 | "sprites":[ 5 | { 6 | "name":"coin-item", 7 | "images":[ 8 | { 9 | "x":0, 10 | "y":7, 11 | "scale":2 12 | }, 13 | { 14 | "x":1, 15 | "y":7, 16 | "scale":2 17 | }, 18 | { 19 | "x":2, 20 | "y":7, 21 | "scale":2 22 | }, 23 | { 24 | "x":3, 25 | "y":7, 26 | "scale":2 27 | } 28 | ], 29 | "deltaTime":10, 30 | "colorKey": -1 31 | } 32 | ] 33 | } 34 | 35 | -------------------------------------------------------------------------------- /sprites/Koopa.json: -------------------------------------------------------------------------------- 1 | { 2 | "spriteSheetURL": "./img/koopas.png", 3 | "type":"character", 4 | "size":[17,32], 5 | "sprites":[ 6 | { 7 | "name":"koopa-1", 8 | "x":1, 9 | "y":0, 10 | "scalefactor":2, 11 | "colorKey":-1, 12 | "collision":false 13 | }, 14 | { 15 | "name":"koopa-2", 16 | "x":17, 17 | "y":0, 18 | "scalefactor":2, 19 | "colorKey":-1, 20 | "collision":false 21 | }, 22 | { 23 | "name":"koopa-hiding", 24 | "x":45, 25 | "y":80, 26 | "scalefactor":2, 27 | "colorKey":-1, 28 | "collision":false 29 | }, 30 | { 31 | "name":"koopa-hiding-with-legs", 32 | "x":163, 33 | "y":206, 34 | "scalefactor":2, 35 | "colorKey":-1, 36 | "collision":false 37 | } 38 | ] 39 | } 40 | 41 | -------------------------------------------------------------------------------- /sprites/Mario.json: -------------------------------------------------------------------------------- 1 | { 2 | "spriteSheetURL": "./img/characters.gif", 3 | "type":"character", 4 | "size":[16,16], 5 | "sprites":[ 6 | { 7 | "name":"mario_idle", 8 | "x":276, 9 | "y":44, 10 | "scalefactor":2, 11 | "colorKey":-1, 12 | "collision":false 13 | }, 14 | { 15 | "name":"mario_run1", 16 | "x":290, 17 | "y":44, 18 | "scalefactor":2, 19 | "colorKey":-1, 20 | "collision":false 21 | }, 22 | { 23 | "name":"mario_run2", 24 | "x":304, 25 | "y":44, 26 | "scalefactor":2, 27 | "colorKey":-1, 28 | "collision":false 29 | }, 30 | { 31 | "name":"mario_run3", 32 | "x":321, 33 | "y":44, 34 | "scalefactor":2, 35 | "colorKey":-1, 36 | "collision":false 37 | }, 38 | { 39 | "name":"mario_break", 40 | "x":337, 41 | "y":44, 42 | "scalefactor":2, 43 | "colorKey":-1, 44 | "collision":false 45 | }, 46 | { 47 | "name":"mario_jump", 48 | "x":355, 49 | "y":44, 50 | "scalefactor":2, 51 | "colorKey":-1, 52 | "collision":false 53 | }, 54 | { 55 | "name":"mario_dead", 56 | "x":355, 57 | "y":44, 58 | "scalefactor":2, 59 | "colorKey":-1, 60 | "collision":false 61 | 62 | } 63 | ] 64 | } 65 | 66 | -------------------------------------------------------------------------------- /traits/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnirankar/Super-Mario-Python/0075e6671dadad757eac156dab069a52506f6d0a/traits/__init__.py -------------------------------------------------------------------------------- /traits/bounce.py: -------------------------------------------------------------------------------- 1 | class bounceTrait: 2 | def __init__(self, entity): 3 | self.vel = 5 4 | self.jump = False 5 | self.entity = entity 6 | 7 | def update(self): 8 | if self.jump: 9 | self.entity.vel.y = 0 10 | self.entity.vel.y -= self.vel 11 | self.jump = False 12 | self.entity.inAir = True 13 | 14 | def reset(self): 15 | self.entity.inAir = False 16 | -------------------------------------------------------------------------------- /traits/go.py: -------------------------------------------------------------------------------- 1 | from pygame.transform import flip 2 | 3 | 4 | class goTrait: 5 | def __init__(self, animation, screen, camera, ent): 6 | self.animation = animation 7 | self.direction = 0 8 | self.heading = 1 9 | self.accelVel = 0.4 10 | self.decelVel = 0.25 11 | self.maxVel = 3.0 12 | self.screen = screen 13 | self.boost = False 14 | self.camera = camera 15 | self.entity = ent 16 | 17 | def update(self): 18 | if self.boost: 19 | self.maxVel = 5.0 20 | self.animation.deltaTime = 4 21 | else: 22 | self.animation.deltaTime = 7 23 | if abs(self.entity.vel.x) > 3.2: 24 | self.entity.vel.x = 3.2 * self.heading 25 | self.maxVel = 3.2 26 | 27 | if self.direction != 0: 28 | self.heading = self.direction 29 | if self.heading == 1: 30 | if self.entity.vel.x < self.maxVel: 31 | self.entity.vel.x += self.accelVel * self.heading 32 | else: 33 | if self.entity.vel.x > -self.maxVel: 34 | self.entity.vel.x += self.accelVel * self.heading 35 | 36 | if not self.entity.inAir: 37 | self.animation.update() 38 | else: 39 | self.animation.inAir() 40 | else: 41 | self.animation.update() 42 | if self.entity.vel.x >= 0: 43 | self.entity.vel.x -= self.decelVel 44 | else: 45 | self.entity.vel.x += self.decelVel 46 | if int(self.entity.vel.x) == 0: 47 | self.entity.vel.x = 0 48 | if self.entity.inAir: 49 | self.animation.inAir() 50 | else: 51 | self.animation.idle() 52 | self.drawEntity() 53 | 54 | def drawEntity(self): 55 | if self.heading == 1: 56 | self.screen.blit(self.animation.image, self.entity.getPos()) 57 | elif self.heading == -1: 58 | self.screen.blit( 59 | flip(self.animation.image, True, False), self.entity.getPos() 60 | ) 61 | -------------------------------------------------------------------------------- /traits/jump.py: -------------------------------------------------------------------------------- 1 | 2 | class jumpTrait: 3 | def __init__(self, entity): 4 | self.vertical_speed = -12 #jump speed 5 | self.jumpHeight = 120 #jump height in pixels 6 | self.entity = entity 7 | self.initalHeight = 384 #stores the position of mario at jump 8 | self.deaccelerationHeight = self.jumpHeight - ((self.vertical_speed*self.vertical_speed)/(2*self.entity.gravity)) 9 | 10 | def jump(self,jumping): 11 | print(self.entity.rect.y) 12 | if jumping: 13 | if not self.entity.inAir and not self.entity.inJump: #only jump when mario is on ground and not in a jump. redundant check 14 | self.entity.sound.play_sfx(self.entity.sound.jump) 15 | self.entity.vel.y = self.vertical_speed 16 | self.entity.inAir = True 17 | self.initalHeight = self.entity.rect.y 18 | self.entity.inJump = True 19 | self.entity.obeygravity = False #dont obey gravity in jump so as to reach jump height no matter what the speed 20 | 21 | if self.entity.inJump: #check vertical distance travelled while mario is in a jump 22 | if (self.initalHeight-self.entity.rect.y) >= self.deaccelerationHeight or self.entity.vel.y==0: 23 | print("reached") 24 | self.entity.inJump = False 25 | self.entity.obeygravity = True #mario obeys gravity again and continues normal play 26 | 27 | 28 | def reset(self): 29 | self.entity.inAir = False 30 | -------------------------------------------------------------------------------- /traits/leftrightwalk.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | 3 | from classes.Collider import Collider 4 | 5 | 6 | class LeftRightWalkTrait: 7 | def __init__(self, entity, level): 8 | self.direction = -1 if randint(0, 1) == 0 else 1 9 | self.entity = entity 10 | self.collDetection = Collider(self.entity, level) 11 | self.speed = 1 12 | self.entity.vel.x = self.speed * self.direction 13 | 14 | def update(self): 15 | if self.entity.vel.x == 0: 16 | self.direction *= -1 17 | self.entity.vel.x = self.speed * self.direction 18 | self.moveEntity() 19 | 20 | def moveEntity(self): 21 | self.entity.rect.y += self.entity.vel.y 22 | self.collDetection.checkY() 23 | self.entity.rect.x += self.entity.vel.x 24 | self.collDetection.checkX() 25 | --------------------------------------------------------------------------------