Exemple #1
0
    def __init__(self, master):
        """Construct a new game of a MarioApp game.

        Parameters:
            master (tk.Tk): tkinter root widget
        """



        #level readed
        self._master = master
        self._master.title('Mario')
        self.down_pressed = False

        self._master.update()
        file_path = filedialog.askopenfilename()   # Openning  a view to let the user choose the configuration file
        filename, file_extension = os.path.splitext(file_path)  # Getting the extension of the selected file
        if file_extension != ".txt" :     # Show an error message box when the extension is Wrong. The extension must be .txt
            messagebox.showerror("Error", "File extension should be .txt")
            exit()

        self.levels = []

        self.load_configuration(file_path) # to load configurations from the configuration file

        world_builder = WorldBuilder(BLOCK_SIZE, gravity=(0, self.gravity), fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder
        self._world = load_world(world_builder, self.level)

        # Create a menu bar on the top of the game and association a function to each button on the menu with command parameter
        menubar = tk.Menu(self._master)
        menubar.add_command(label = "Load Level", command = self.showDialogInput)
        menubar.add_command(label = "Reset Level", command = self.resetLevel)
        menubar.add_command(label = "High scores", command = self.displayHighScores)
        menubar.add_command(label = "Exit", command = self.exitGame)
        self._master.config(menu = menubar)

        self._player = Player(name = self.character, max_health=self.max_health)
        self._player.invincible = False
        self._world.add_player(self._player, self.starting_x, self.starting_y, mass = self.mass)

        self._setup_collision_handlers()

        self._renderer = MarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES, MOB_IMAGES)

        size = tuple(map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(master, size, self._renderer)
        self._view.pack()
        self.bind()

        # Wait for window to update before continuing
        master.update_idletasks()

        self.statusBar = StatusBar(self._master)

        self.step()
        self._master.mainloop()
Exemple #2
0
    def __init__(self, master: tk.Tk):
        """Construct a new game of a MarioApp game.

        Parameters:
            master (tk.Tk): tkinter root widget
        """
        self._master = master
        # default configuration setting
        self._level = "level1.txt"
        self._gravity = (0, 300)
        self._max_health = 5
        self._mass = 100
        self._x = BLOCK_SIZE
        self._y = BLOCK_SIZE
        self._max_velocity = 500
        self._config = {}

        self._master.update_idletasks()
        self.load_config()

        world_builder = WorldBuilder(BLOCK_SIZE,
                                     self._gravity,
                                     fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder

        self._player = Player(max_health=self._max_health)
        self._player.set_jumping(True)
        self._player.set_shoot(False)

        self.reset_world(self._level)

        self._renderer = MarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES,
                                           MOB_IMAGES)

        size = tuple(
            map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(master, size, self._renderer)
        self._view.pack()

        self.bind()

        self.menu_bar()

        # create the character status bar
        self.status_bar = Status(master)
        self.status_bar.pack()

        # Wait for window to update before continuing
        master.update_idletasks()
        self.step()
Exemple #3
0
def play(cfg: Config):
    pg.init()
    game = Game(**cfg.get_game_config())
    controller = GameController(game)
    view = GameView(game, **cfg.get_view_config())
    clock = pg.time.Clock()
    while game.is_running:
        clock.tick(cfg.fps)
        controller.handle_user_input()
        if not controller.pause and game.is_running:
            game.run()
            view.update()
    pg.quit()
Exemple #4
0
    def _start_game(self):
        path = tk.simpledialog.askstring("Mario", "Configuration file:")
        self._game = loader.load_level(path)
        if self._game == None:
            tk.messagebox.showerror("Error", "Configuration file wrong.")
            self._master.destroy()

        # World builder
        world_builder = WorldBuilder(
            BLOCK_SIZE,
            gravity=(0, int(self._game["==World=="]["gravity"])),
            fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder

        self._current_level = self._game["==World=="]['start']
        self._world = load_world(self._builder, self._current_level)

        # Set tunnel/goal
        Flagpole._next_level = self._game[("==" + self._current_level +
                                           "==")]["goal"]
        Tunnel._next_level = self._game[("==" + self._current_level +
                                         "==")].get("tunnel", None)

        self._player = Player(
            max_health=int(self._game["==Player=="]["health"]))
        print(self._player.get_shape())
        self._world.add_player(self._player,
                               int(self._game["==Player=="]["x"]),
                               int(self._game["==Player=="]["y"]),
                               int(self._game["==Player=="]["mass"]))

        self._max_speed = int(self._game["==Player=="]["max_velocity"])

        self._setup_collision_handlers()

        self._renderer = AnimatedMarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES,
                                                   MOB_IMAGES)  # Into Animated

        size = tuple(
            map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(self._master, size, self._renderer)
        self._view.pack()

        self._status_view = StatusView(self._master,
                                       int(self._game["==Player=="]['health']),
                                       0)

        self.bind()
Exemple #5
0
    def __init__(self, master: tk.Tk) :
        """Construct a new game of a MarioApp game.

        Parameters:
            master (tk.Tk): tkinter root widget
        """
        self._master = master
        config_name = askopenfilename(filetypes=( ("Text file", "*.txt"),("HTML files", "*.html;*.htm")))
        self.load_config(config_name)
        self._gravity
        self._level_name
        self._healthdeterminant
        world_builder = WorldBuilder(BLOCK_SIZE, (0,300), fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder

        self._player = Player(max_health= self._health)
        self._powerup = PowerUp()
        self.reset_world(self._level_name)

        #self._renderer = MarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES, MOB_IMAGES)
        self._sprite_renderer = SpriteSheetView(BLOCK_IMAGES, ITEM_IMAGES, MOB_IMAGES)
        size = tuple(map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(master, size, self._sprite_renderer)
        self._view.pack()

        self.bind()

        # Wait for window to update before continuing
        master.update_idletasks()

        self._status_view = StatusView(master, self._health)
        self._status_view.pack()
        self._status_view.update_score(self._player.get_score())
        self._status_view.update_health(self._player.get_health())

        self._setup_collision_handlers()
        master.update_idletasks()
        self._start_time = 0
        self.step()
        self.menu = MenuBar(master, [("File", {"Reset" : self._reset,
                                               "Load Level" : self._import_new_level,
                                               "Exit" : self._close,
                                               "High Score": self._status_view.high})])
        self._collide = False
Exemple #6
0
    def reset_world(self, new_level=None, full=False):
        if new_level == None:
            new_level = self._current_level
        else:
            self._current_level = new_level
            self._scores = HighScores()
        world_builder = WorldBuilder(BLOCK_SIZE,
                                     gravity=(0, 500),
                                     fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder
        self._world = load_world(self._builder, new_level)

        Flagpole._next_level = self._game[("==" + self._current_level +
                                           "==")]["goal"]
        Tunnel._next_level = self._game[("==" + self._current_level +
                                         "==")].get("tunnel", None)

        if full:  # full reset player stats
            self._player = Player(
                max_health=int(self._game["==Player=="]["health"]))
        self._world.add_player(self._player,
                               int(self._game["==Player=="]["x"]),
                               int(self._game["==Player=="]["y"]),
                               int(self._game["==Player=="]["mass"]))

        self._setup_collision_handlers()

        self._view.pack_forget()
        size = tuple(
            map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(self._master, size, self._renderer)
        self._view.pack()

        self._status_view.unpack()
        self._status_view = StatusView(self._master,
                                       int(self._game["==Player=="]['health']),
                                       self._player._score)
Exemple #7
0
    def __init__(self, master: tk.Tk):
        """Construct a new game of a MarioApp game.

        Parameters:
            master (tk.Tk): tkinter root widget
        """
        self._master = master
        self._master.title("Mario bird")

        world_builder = WorldBuilder(BLOCK_SIZE, gravity=(0, 300), fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder

        self._player = Player(max_health=5)
        self.reset_world('level1.txt')
        self._level_holder = 'level1.txt'

        self._renderer = MarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES, MOB_IMAGES)

        size = tuple(map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(master, size, self._renderer)
        self._view.pack()
        self.bind()
        self.menubar()
        #Stats controller for changing the status bar.
        #used in the function 'step'
        self._health = self._player.get_health()
        self._score = self._player.get_score()
        self._status_bar = tk.Frame(self._master)
        self.status_bar()
        # Wait for window to update before continuing
        master.update_idletasks()
        self.step()
        self._death_action = False
Exemple #8
0
class MarioApp:
    """High-level app class for Mario, a 2d platformer"""


    def setWorldProperties(self, propertie, value):

        if propertie == "gravity":
            self.gravity = int(value)
        elif propertie == "start":
            self.level = value
        else:
            self.alertFile()

    def setPlayerProperties(self, propertie, value):
        if propertie == "character":
            self.character = value
        elif propertie == "x":
            self.starting_x = int(value)
        elif propertie == "y":
            self.starting_y = int(value)
        elif propertie == "mass":
            self.mass = int(value)
        elif propertie == "health":
            self.max_health = int(value)
        elif propertie == "max_velocity":
            self.max_velocity = int(value)
        else:
            self.alertFile()

    def setLevelProperties(self, currentLevel, propertie, value):

        for level in self.levels: #iterate over the levels to get the level having the name currentName
            if level.name == currentLevel:
                theLevel = level

        if propertie == "tunnel":
            theLevel.tunnel = value
        elif propertie == "goal":
            theLevel.goal = value
        else:
            self.alertFile()

    def alertFile(self): #the alert is shown when there is wrong properties on the configuration file or on missing properties or if the file is emply
        messagebox.showerror("Warning", "Wrong configuration file")
        exit()


    def __init__(self, master):
        """Construct a new game of a MarioApp game.

        Parameters:
            master (tk.Tk): tkinter root widget
        """



        #level readed
        self._master = master
        self._master.title('Mario')
        self.down_pressed = False

        self._master.update()
        file_path = filedialog.askopenfilename()   # Openning  a view to let the user choose the configuration file
        filename, file_extension = os.path.splitext(file_path)  # Getting the extension of the selected file
        if file_extension != ".txt" :     # Show an error message box when the extension is Wrong. The extension must be .txt
            messagebox.showerror("Error", "File extension should be .txt")
            exit()

        self.levels = []

        self.load_configuration(file_path) # to load configurations from the configuration file

        world_builder = WorldBuilder(BLOCK_SIZE, gravity=(0, self.gravity), fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder
        self._world = load_world(world_builder, self.level)

        # Create a menu bar on the top of the game and association a function to each button on the menu with command parameter
        menubar = tk.Menu(self._master)
        menubar.add_command(label = "Load Level", command = self.showDialogInput)
        menubar.add_command(label = "Reset Level", command = self.resetLevel)
        menubar.add_command(label = "High scores", command = self.displayHighScores)
        menubar.add_command(label = "Exit", command = self.exitGame)
        self._master.config(menu = menubar)

        self._player = Player(name = self.character, max_health=self.max_health)
        self._player.invincible = False
        self._world.add_player(self._player, self.starting_x, self.starting_y, mass = self.mass)

        self._setup_collision_handlers()

        self._renderer = MarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES, MOB_IMAGES)

        size = tuple(map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(master, size, self._renderer)
        self._view.pack()
        self.bind()

        # Wait for window to update before continuing
        master.update_idletasks()

        self.statusBar = StatusBar(self._master)

        self.step()
        self._master.mainloop()


        #DEBUT

    def load_configuration(self, file_path):
        with open(file_path) as fp:
            line = fp.readline()
            while line and "==World==" not in line:
                line = fp.readline()

            if not line:
                self.alertFile()

            # World line readed
            line = fp.readline()
            while line and "==Player==" not in line:
                if (":" in line):
                    self.setWorldProperties(line.split(':')[0].strip(), line.split(':')[1].strip())
                line = fp.readline()
            if not line:
                self.alertFile()

            line = fp.readline()
            while line and self.level not in line:
                if (":" in line):
                    self.setPlayerProperties(line.split(':')[0].strip(), line.split(':')[1].strip())
                line = fp.readline()

            if not line:
                self.alertFile()

            current_level = self.level

            while (line):
                self.levels.append(Level(current_level))
                line = fp.readline()
                while (line and "==" not in line):
                    if (":" in line):
                        self.setLevelProperties(current_level, line.split(':')[0].strip(), line.split(':')[1].strip())
                    line = fp.readline()
                if ("==" in line):
                    current_level = line.strip()

    def left_key(self, event): # Move Mario on the left when pressing left keypad
        self._move(-50, 0)

    def right_key(self, event):  # Move Mario on the right when pressing right keypad
        self._move(50, 0)

    def up_key(self, event):  # Move Mario up when pressing up keypad
        self._jump()

    def down_key(self, event):  # Move Mario down when pressing down keypad
        self._duck()
    #FIN
    def reset_world(self, new_level="level1.txt"):
        self.level = new_level
        self._world = load_world(self._builder, new_level)
        health = self._player.get_health()
        self._player = Player(max_health=5)
        self._player.invincible = False
        self.down_pressed = False
        self._player.change_health(health - self._player.get_max_health())
        self._world.add_player(self._player, BLOCK_SIZE, BLOCK_SIZE)

        self._setup_collision_handlers()


    def bind(self):
        #LEFT
        self._master.bind('<Left>', self.left_key)
        self._master.bind('L', self.left_key)
        #RIGHT
        self._master.bind('<Right>', self.right_key)
        self._master.bind('R', self.right_key)
        #UP
        self._master.bind('<Up>', self.up_key)
        self._master.bind('W', self.up_key)
        self._master.bind('<space>', self.up_key)
        #DOWN
        self._master.bind('<Down>', self.down_key)
        self._master.bind('S', self.down_key)



    def redraw(self):
        """Redraw all the entities in the game canvas."""
        self._view.delete(tk.ALL)

        self._view.draw_entities(self._world.get_all_things())

    #Show a dialog input after load Option in the menu.
    #The input should be an integer which coressponds to an existing Level. In our case Level 1 and 2
    # If the input is correct, we load the new level file
    def showDialogInput(self) :
        answer = simpledialog.askstring("Input", "Level number : ",parent=self._master)
        nameLevelTxt = "level"
        while(answer is None or (answer != "1" and answer !="2")) :
            answer = simpledialog.askstring("Input", " enter a correct Level number (1 or 2)  : ",parent=self._master)
        nameLevelTxt = nameLevelTxt +answer + ".txt"
        self.reset_world(nameLevelTxt)

    # Reset Score and Health of Mario
    def resetLevel(self) :
        self._player.change_score(-1*self._player.get_score())  #change score method add the parameter to the current score, so to reset the score to 0 we have just to add the -1* the currentScore
        self._player.change_health(self._player.get_max_health() - self._player.get_health()) #same for health, to reset the health to the maxHealth we have just to add the max_health - health


    def displayHighScores(self):

        top = tk.Toplevel()
        top.title("High Scores")
        try:
            f = open("HS" + self.level, "r")
        except IOError:
            f= open("HS" + self.level,"w+")

        scores = []

        f = open("HS" + self.level, "r")
        line = f.readline()
        counter = 1
        message = "" # variable containing the string to show in the message
        while line and counter < 11:
            if 'SEP' in line: #we re separating the player name and his score with the SEP
                message += str(counter) + " - " + line.split("SEP")[0].strip() + " : " + line.split("SEP")[1].strip() + "\n"
                counter = counter + 1
            line = f.readline()


        msg = Message(top, text=message, width=200)
        msg.config(font=("Courier", 12))
        msg.pack()

        button = tk.Button(top, text="Ok", command=top.destroy)
        button.pack()
        pass

    # Closing the gameView after selecting this option of menubar
    def exitGame(self):
        exit()

    def scroll(self):
        """Scroll the view along if the player is within SCROLL_RADIUS
        from the edge of the screen.
        """
        # calculate the x distance from the right edge
        x_position = self._player.get_position()[0]
        x_offset = self._view.get_offset()[0]
        screen_size = self._master.winfo_width()
        edge_distance = screen_size - (x_position + x_offset)

        if edge_distance < SCROLL_RADIUS:
            x_position -= 5

            # place a backstop boundary wall on the left side of the screen
            # to prevent the player from going backwards in the game
            world_space = self._world.get_space()
            wall = BoundaryWall("backstop", world_space.static_body,
                                (x_position, 0),
                                (x_position, self._world.get_pixel_size()[1]), 5)
            world_space.add(wall.get_shape())

            # shift the view offset by the screen size
            self._view.shift((-(screen_size - SCROLL_RADIUS), 0))

    def step(self):
        """Step the world physics and redraw the canvas."""
        data = (self._world, self._player)


        self._world.step(data)

        self.scroll()
        self.redraw()


        self.statusBar.scoreStr.set("score = " + str(self._player.get_score())) #to refresh the scoreLabel on each step
        self.statusBar._scoreLabel.pack()

        ratio = 1.0 * self._player.get_health() / self._player.get_max_health()

        #Configuring the status bar depending on health and Mario's state (Normal or invincible)
        if self._player.invincible == True :
            self.statusBar._HealthLabel.configure(bg="yellow")
        else :
            if(ratio >= 0.5):
                self.statusBar._HealthLabel.configure(bg="green")
            else:
                if(ratio > 0.25):
                    self.statusBar._HealthLabel.configure(bg="orange")
                else:
                    self.statusBar._HealthLabel.configure(bg="red")

        #in case if the health changes we have to refresh the healthLabel
        healthWidth = int(155.0 * ratio) # healthWidth change depending on the health
        self.statusBar._HealthLabel.config(width=healthWidth, height=1)
        self.statusBar._HealthLabel1.config(width=155 - healthWidth, height=1)
        self.statusBar._HealthLabel.pack(side=tk.LEFT)
        self.statusBar._HealthLabel1.pack(side=tk.LEFT)

        self._master.after(10, self.step)



    def _move(self, vx, vy):
        if(vx < 0): #moving left
            if(abs(vx) > self.max_velocity): # we have to check if velocity we set in inferior than the max velocity variable defined in the configuration file
                vx = -1 * self.max_velocity

            self._player.set_velocity((vx, 0))

        else: #moving right
            if(vx > self.max_velocity):
                vx = self.max_velocity

            self._player.set_velocity((vx, 0))


    def _jump(self):
        vy = -130
        if(vy > abs(self.max_velocity)):
            vy = -1 * self.max_velocity

        self._player.set_velocity((0, vy))


    def _duck(self):
        self.down_pressed = True
        vy = 130
        if(vy > self.max_velocity):
            vy = self.max_velocity

        self._player.set_velocity((0, vy))


    def _setup_collision_handlers(self):
        self._world.add_collision_handler("player", "item", on_begin=self._handle_player_collide_item)
        self._world.add_collision_handler("player", "block", on_begin=self._handle_player_collide_block,
                                          on_separate=self._handle_player_separate_block)
        self._world.add_collision_handler("player", "mob", on_begin=self._handle_player_collide_mob)
        self._world.add_collision_handler("mob", "block", on_begin=self._handle_mob_collide_block)
        self._world.add_collision_handler("mob", "mob", on_begin=self._handle_mob_collide_mob)
        self._world.add_collision_handler("mob", "item", on_begin=self._handle_mob_collide_item)



    def _handle_mob_collide_block(self, mob: Mob, block: Block, data,
                                  arbiter: pymunk.Arbiter) -> bool:
        if(mob.get_id() == "mushroom"): #to change the mob direction when he touch a block
            if get_collision_direction(block, mob) == "L" or get_collision_direction(block, mob) == "R":
                mob.set_tempo(-1 * mob.get_tempo())

        if mob.get_id() == "fireball":
            if block.get_id() == "brick":
                self._world.remove_block(block)
            self._world.remove_mob(mob)
        return True


    def _handle_mob_collide_item(self, mob: Mob, block: Block, data,
                                 arbiter: pymunk.Arbiter) -> bool:
        return False

    def _handle_mob_collide_mob(self, mob1: Mob, mob2: Mob, data,
                                arbiter: pymunk.Arbiter) -> bool:
        if(mob1.get_id() == "mushroom"):
            mob1.set_tempo(-1 * mob1.get_tempo()) #change the mob direction => reverse the tempo
        if(mob2.get_id() == "mushroom"):
            mob2.set_tempo(-1 * mob2.get_tempo())  #change the mob direction

        if mob1.get_id() == "fireball" or mob2.get_id() == "fireball":
            self._world.remove_mob(mob1)
            self._world.remove_mob(mob2)

        return False

    def _handle_player_collide_item(self, player: Player, dropped_item: DroppedItem,
                                    data, arbiter: pymunk.Arbiter) -> bool:
        """Callback to handle collision between the player and a (dropped) item. If the player has sufficient space in
        their to pick up the item, the item will be removed from the game world.

        Parameters:
            player (Player): The player that was involved in the collision
            dropped_item (DroppedItem): The (dropped) item that the player collided with
            data (dict): data that was added with this collision handler (see data parameter in
                         World.add_collision_handler)
            arbiter (pymunk.Arbiter): Data about a collision
                                      (see http://www.pymunk.org/en/latest/pymunk.html#pymunk.Arbiter)
                                      NOTE: you probably won't need this
        Return:
             bool: False (always ignore this type of collision)
                   (more generally, collision callbacks return True iff the collision should be considered valid; i.e.
                   returning False makes the world ignore the collision)
        """

        dropped_item.collect(self._player)
        self._world.remove_item(dropped_item)
        return False

    def getNextTunnelLevel(self, current): #return the nextLevel if the player touched the tunnel using the config file
        for level in self.levels:
            if(level.name == current):
                return level.tunnel

    def getNextLevel(self, current): #return the nextLevel using the config file
        for level in self.levels:
            if(level.name == current):
                return level.goal

    def addScore(self):

        name = simpledialog.askstring("Input", "Your name : ",parent=self._master) #ask the user to insert his name
        while(name is None) :
            name = simpledialog.askstring("Input", " Your name : ",parent=self._master)

        try:
            f = open("HS" + self.level, "r")
        except IOError:  #if the file don"t exist
            f= open("HS" + self.level,"w+")

        f.close()

        scores = [] #we add all the scores sorted in this list respecting the order of the new score

        f = open("HS" + self.level, "r")
        line = f.readline()
        done = False
        while line :#iterate over the lines to find the line where we should insert the score
            if 'SEP' in line:
                if not done and self._player.get_score() > int(line.split("SEP")[1].strip()):
                    scores.append((name, self._player.get_score()))
                    done = True
                scores.append((line.split("SEP")[0].strip() , int(line.split("SEP")[1].strip())))
            line = f.readline()

        if not done: #in case if the file was emply or the user's score is less than all the others
            scores.append((name, self._player.get_score()))

        f.close()

        f= open("HS" + self.level,"w")

        for sc in scores:
            name, score = sc
            f.write(name + " SEP " + str(score) + "\n")

        f.close

    def _handle_player_collide_block(self, player: Player, block: Block, data,
                                     arbiter: pymunk.Arbiter) -> bool:

        if(block.get_id() == "bounce"):  # Propel Mario when collide to BounceBlock
            self._player.set_velocity((0, -180))

        if(block.get_id()== "flag"):
            self.addScore() #to let the player enter his name to add the score on the highscore file
            self.level = self.getNextLevel(self.level) #get the nextLevel using the config File
            self.reset_world(self.level)
            if(get_collision_direction(player, block) == "A"): #increase the player health in case his on top of the flag
                player.change_health(1)

        if(block.get_id()=="tunnel" and get_collision_direction(player, block) == "A" and self.down_pressed is True): #GO THE NEXT LEVEL IF the player pressed up on top of the tunnel
            self.level = self.getNextTunnelLevel(self.level)
            self.reset_world(self.level)

        #Removing the bricks on the left and on the right of the switch
        if block.get_id() == "switch" and get_collision_direction(player, block) == "A":
            x,y = block.get_position()
            block1 = self._world.get_block(x-GRID_WIDTH,y-block.getCollNum()*GRID_HEIGHT)
            block2 = self._world.get_block(x+GRID_WIDTH,y+block.getCollNum()*GRID_HEIGHT)
            self._world.remove_block(block1)
            self._world.remove_block(block2)
            block.incrementcollNum()

        block.on_hit(arbiter, (self._world, player))
        return True

    def _handle_player_collide_mob(self, player: Player, mob: Mob, data,
                                   arbiter: pymunk.Arbiter) -> bool:
        # A collision with a mob make Mario Lost a health.
        # When Mario is Invincible Mario Lost and gain a life.
        # Thus, the health don't change tough the collision heppened
        if player.invincible == True :
            player.change_health(1)

        if(type(mob) is MushroomMob):
            if get_collision_direction(player, mob) == "A": #in case if the player is above the mushroomMob
                player.set_velocity((0, 100)) #the player up
                mob.destroy() #destroy the mob
            else:#in case the collision is on another direction
                player.change_health(-1) #the player should lose health
                player.set_velocity((2 * mob.get_velocity()[0], 0)) , #the player should be slightly repelled away

        mob.on_hit(arbiter, (self._world, player))
        return True

    def _handle_player_separate_block(self, player: Player, block: Block, data,
                                      arbiter: pymunk.Arbiter) -> bool:
        return True
Exemple #9
0
class MarioApp:
    """High-level app class for Mario, a 2d platformer"""
    _world: World

    def __init__(self, master: tk.Tk):
        """Construct a new game of a MarioApp game.

        Parameters:
            master (tk.Tk): tkinter root widget
        """
        self._master = master
        # default configuration setting
        self._level = "level1.txt"
        self._gravity = (0, 300)
        self._max_health = 5
        self._mass = 100
        self._x = BLOCK_SIZE
        self._y = BLOCK_SIZE
        self._max_velocity = 500
        self._config = {}

        self._master.update_idletasks()
        self.load_config()

        world_builder = WorldBuilder(BLOCK_SIZE,
                                     self._gravity,
                                     fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder

        self._player = Player(max_health=self._max_health)
        self._player.set_jumping(True)
        self._player.set_shoot(False)

        self.reset_world(self._level)

        self._renderer = MarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES,
                                           MOB_IMAGES)

        size = tuple(
            map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(master, size, self._renderer)
        self._view.pack()

        self.bind()

        self.menu_bar()

        # create the character status bar
        self.status_bar = Status(master)
        self.status_bar.pack()

        # Wait for window to update before continuing
        master.update_idletasks()
        self.step()

    def read_config(self, filename: str):
        """
        To read the configuration data from the txt file
        Parameter:
            filename (str): filename
        Return (dictionary): looks like {"level":{'key':value, 'key': value},}
        """
        config = {}
        with open(filename) as hand:
            for line in hand:
                line = line.rstrip()
                if line.startswith("==") and line.endswith("=="):
                    # heading line
                    heading = line[2:-2]
                    config[heading] = {}
                else:
                    # attribute line
                    attr, _, value = line.partition(' : ')
                    config[heading][attr] = value
        hand.close()
        return config

    def load_config(self):
        """
        load the read configuration to the default settings
        If the configuration file is invalid, exit the game with an error message.
        """
        config_file = filedialog.askopenfilename()
        try:
            config = self.read_config(config_file)
            self._config = config
            self._level = config['World']['start']
            self._gravity = (0, int(config['World']['gravity']))
            self._x = float(config['Player']['x'])
            self._y = float(config['Player']['y'])
            self._mass = int(config['Player']['mass'])
            self._max_health = int(config['Player']['health'])
            self._max_velocity = int(config['Player']['max_velocity'])
        except UnboundLocalError:
            tk.messagebox.showerror('Error', 'Bad Input')
            self._master.destroy()

    def reset_world(self, new_level):
        self._world = load_world(self._builder, new_level)
        self._world.add_player(self._player, self._x, self._y, self._mass)
        self._builder.clear()

        self._setup_collision_handlers()

    def menu_bar(self):
        """
        Create a menu bar
        """
        menubar = tk.Menu(self._master)
        self._master.config(menu=menubar)
        # within the menu bar create the file menu
        filemenu = tk.Menu(menubar)
        menubar.add_cascade(label="File", menu=filemenu)
        # within the file menu create the file processing options
        filemenu.add_command(label="Load Level", command=self.load_level)
        filemenu.add_command(label="Reset Level", command=self.reset_level)
        filemenu.add_command(label="High Score", command=self.show_scores)
        filemenu.add_command(label="Exit", command=self.exit)

    def load_level(self):
        """
        Input a level file and load that level
        """
        filename = filedialog.askopenfilename()
        if filename:
            self.reset_world(filename)
        self._level = filename

    def reset_level(self):
        """
        Reset the current level and all player progress
        """
        ans = messagebox.askokcancel('Restart Game', 'Restart Game?')
        if ans:
            self.reset_world(self._level)
            self._player.clear_score()
            self._player.change_health(self._player.get_max_health())
            self.redraw_status()
        else:
            self._master.destroy()

    def exit(self):
        """
        exit the game
        """
        ans = messagebox.askokcancel('Exit Game', 'Really exit?')
        if ans:
            self._master.destroy()

    def game_over(self):
        """
        See if the player is dead. If so, ask if want to start a new game or just quit.
        """
        if self._player.is_dead():
            ans = messagebox.askokcancel('Player is dead', 'Start Over?')
            if ans:
                self.reset_world('level1.txt')
                self._level = 'level1.txt'
                self._player.clear_score()
                self._player.change_health(self._player.get_max_health())
                self.redraw_status()
            else:
                self._master.destroy()

    def read_score(self):
        """
        read the score records from the text file
        Return:
            score (dictionary): looks like this {'level':[('name', int), ('name', int), ('name', int), ], }
        """
        score = {}
        with open("high_score.txt") as hand:
            for line in hand:
                line = line.rstrip()
                if line.startswith("**") and line.endswith("**"):
                    heading = line[2:-2]
                    score[heading] = []
                else:
                    record = line.split(' : ')
                    score[heading].append((record[0], int(record[1])))
        hand.close()
        return score

    def update_score(self):
        """
        ask for the name of the current player and get the current score.
        read the high score records and see if the current player gets into the top 10 high scores for the current level
        if so, update the high score text file with the lowest one replace
        """
        name = tk.simpledialog.askstring("your name",
                                         "what's your name",
                                         parent=self._master)  # 这人名字
        score = self._player.get_score()
        score_records = self.read_score()
        # rank the record list of the current level from low to high by score in the dictionary
        score_records[self._level].sort(key=lambda x: x[1])
        if len(score_records[self._level]) < 10:
            score_records[self._level].append((name, score))
        elif score > score_records[self._level][0][1]:
            score_records[self._level][0] = (
                name, score)  # replace the one with the lowest score

        with open("high_score.txt",
                  'w') as handle:  # write back to the text file
            for k, v in score_records.items():
                handle.write('**{}**\n'.format(k))
                for n in v:
                    name, value = n
                    handle.write('{} : {}\n'.format(name, value))
        handle.close()

    def show_scores(self):
        """
        Display the score records in a window
        """
        score = self.read_score()
        score_window = tk.Toplevel(self._master)
        score_window.geometry('300x200')
        score_window.title(
            self._level.rstrip(".txt").capitalize() + ' Top 10 Scores')

        tk.Label(score_window,
                 text="Top 10 Scores In This Level").pack(side=tk.TOP)
        tk.Label(score_window,
                 text="\n".join(
                     'name:{}\tscore: {}'.format(k, v)
                     for (k, v) in score[self._level])).pack(side=tk.TOP)

    def get_next_level(self):
        """
        (str) Return the string of next level file name
        """
        return self._config[self._level]['goal']

    def load_next_level(self):
        """load the next level in world"""
        self.reset_world(self.get_next_level())

    def bind(self):
        """Bind all the keyboard events to their event handlers."""
        self._master.bind("<a>", self.key_press)
        self._master.bind("<Left>", self.key_press)
        self._master.bind("<d>", self.key_press)
        self._master.bind("<Right>", self.key_press)
        self._master.bind("<w>", self.key_press)
        self._master.bind("<Up>", self.key_press)
        self._master.bind("<space>", self.key_press)
        self._master.bind("<s>", self.key_press)
        self._master.bind("<Down>", self.key_press)
        self._master.bind("<b>", self.key_press)

    def key_press(self, e):
        """
        What to execute when certain key is pressed
        """
        key = e.keysym
        if key == 'a' or key == 'Left':
            self._move(-150, 0)
        elif key == 'd' or key == 'Right':
            self._move(150, 0)
        elif key == 'w' or key == 'Up' or key == 'space':
            self._jump()
        elif key == 's' or key == 'Down':
            self._duck()
        elif key == "b":
            self.shoot()

    def redraw_status(self):
        """
        Redraw the player status bar with the updated health and score value
        """
        self.status_bar.clear()
        self.status_bar.update_health(self._player.get_health(),
                                      self._player.is_niubi(), self._player)
        self.status_bar.update_score(self._player.get_score())

    def redraw(self):
        """Redraw all the entities in the game canvas."""
        self._view.delete(tk.ALL)
        self._view.draw_entities(self._world.get_all_things())
        self.redraw_status()

    def scroll(self):
        """Scroll the view along with the player in the center unless
        they are near the left or right boundaries
        """
        x_position = self._player.get_position()[0]
        half_screen = self._master.winfo_width() / 2
        world_size = self._world.get_pixel_size()[0] - half_screen

        # Left side
        if x_position <= half_screen:
            self._view.set_offset((0, 0))

        # Between left and right sides
        elif half_screen <= x_position <= world_size:
            self._view.set_offset((half_screen - x_position, 0))

        # Right side
        elif x_position >= world_size:
            self._view.set_offset((half_screen - world_size, 0))

    def step(self):
        """Step the world physics and redraw the canvas."""
        data = (self._world, self._player)
        self._world.step(data)

        self.scroll()
        self.redraw()
        self.game_over()
        self._master.after(10, self.step)  # refresh

    def _move(self, dx: int, dy: int):
        """
        move the player
        Parameter:
            dx (int): velocity on x axis
            dy (int): velocity on y axis
        """
        self._player.set_velocity((dx, dy))

    def _jump(self):
        """
        if the player is not jumping, make it jump, and change the jumping status to True.
        """
        if not self._player.is_jumping():
            self._move(0, -200)
            self._player.set_jumping(True)

    def _duck(self):
        """
        set the duck status of the player to True
        """
        self._player.set_duck(True)

    def shoot(self):
        """
        player shoots the bullet
        """
        x, y = self._player.get_position()
        vx, vy = self._player.get_velocity()
        if self._player.is_shoot:
            if vx >= 0:
                self._world.add_mob(BulletRight(), x + 16, y)
            else:
                self._world.add_mob(BulletLeft(), x - 16, y)
        else:
            print('不射')

    def _setup_collision_handlers(self):
        self._world.add_collision_handler(
            "player", "item", on_begin=self._handle_player_collide_item)
        self._world.add_collision_handler(
            "player",
            "block",
            on_begin=self._handle_player_collide_block,
            on_separate=self._handle_player_separate_block)
        self._world.add_collision_handler(
            "player", "mob", on_begin=self._handle_player_collide_mob)
        self._world.add_collision_handler(
            "mob", "block", on_begin=self._handle_mob_collide_block)
        self._world.add_collision_handler(
            "mob", "mob", on_begin=self._handle_mob_collide_mob)
        self._world.add_collision_handler(
            "mob", "item", on_begin=self._handle_mob_collide_item)

    def _handle_mob_collide_block(self, mob: Mob, block: Block, data,
                                  arbiter: pymunk.Arbiter) -> bool:
        if mob.get_id() == "fireball" or mob.get_id(
        ) == 'bullet_l' or mob.get_id() == 'bullet_r':
            if block.get_id() == "brick":
                self._world.remove_block(block)
                self._world.remove_mob(mob)
            else:
                self._world.remove_mob(mob)
        elif mob.get_id(
        ) == "mushroom":  # mushroom bounces back a little when encountering blocks
            if get_collision_direction(
                    mob, block) == "R" or get_collision_direction(
                        mob, block) == "L":
                mob.set_tempo(-mob.get_tempo())
        elif mob.get_id(
        ) == 'gang':  # gang jumps over the blocks when encountering them
            if get_collision_direction(mob, block) == "R":
                mob.set_velocity((50, -350))
            elif get_collision_direction(mob, block) == "L":
                mob.set_velocity((-50, -350))

        return True

    def _handle_mob_collide_item(self, mob: Mob, block: Block, data,
                                 arbiter: pymunk.Arbiter) -> bool:
        return False

    def _handle_mob_collide_mob(self, mob1: Mob, mob2: Mob, data,
                                arbiter: pymunk.Arbiter) -> bool:
        if mob1.get_id() == "fireball" or mob2.get_id() == "fireball":
            self._world.remove_mob(mob1)
            self._world.remove_mob(mob2)
        elif mob1.get_id(
        ) == 'bullet_l' or mob1.get_id == 'bullet_r' or mob2.get_id(
        ) == 'bullet_l' or mob2.get_id == 'bullet_r':
            self._world.remove_mob(mob1)
            self._world.remove_mob(mob2)
        elif mob1.get_id() == "gang" and mob2.get_id() == "mushroom":
            return False
        elif mob1.get_id() == "mushroom" and mob2.get_id() == "gang":
            return False
        elif mob1.get_id() == "gang" and mob2.get_id() == "gang":
            return False
        elif mob1.get_id() == "mushroom" and mob2.get_id() == "mushroom":
            mob1.set_tempo(-mob1.get_tempo())
            mob2.set_tempo(-mob2.get_tempo())
        else:
            self._world.remove_mob(mob1)
            self._world.remove_mob(mob2)

        return False

    def _handle_player_collide_item(self, player: Player,
                                    dropped_item: DroppedItem, data,
                                    arbiter: pymunk.Arbiter) -> bool:
        """Callback to handle collision between the player and a (dropped) item. If the player has sufficient space in
        their to pick up the item, the item will be removed from the game world.

        Parameters:
            player (Player): The player that was involved in the collision
            dropped_item (DroppedItem): The (dropped) item that the player collided with
            data (dict): data that was added with this collision handler (see data parameter in
                         World.add_collision_handler)
            arbiter (pymunk.Arbiter): Data about a collision
                                      (see http://www.pymunk.org/en/latest/pymunk.html#pymunk.Arbiter)
                                      NOTE: you probably won't need this
        Return:
             bool: False (always ignore this type of collision)
                   (more generally, collision callbacks return True iff the collision should be considered valid; i.e.
                   returning False makes the world ignore the collision)
        """

        if dropped_item.get_id() == 'coin':
            dropped_item.collect(self._player)
            self._world.remove_item(dropped_item)
        elif dropped_item.get_id() == 'star':
            dropped_item.collect(self._player)
            self._world.remove_item(dropped_item)
        elif dropped_item.get_id() == 'flower':
            dropped_item.collect(self._player)
            self._world.remove_item(dropped_item)
        return False

    def _handle_player_collide_block(self, player: Player, block: Block, data,
                                     arbiter: pymunk.Arbiter) -> bool:

        if get_collision_direction(
                player, block
        ) == "A":  # when player touch the blocks, set jumping to false
            self._player.set_jumping(False)

        if block.get_id() == "flag":
            if get_collision_direction(player, block) == "A":
                block.on_hit(arbiter, data)
            else:
                # tell the player to input their name and see if the score records need to be updated
                self.update_score()
                if self.get_next_level(
                ) == 'END':  # if there's no further level, ask if start over
                    ans = messagebox.askokcancel(
                        'Good job, you finish the game', 'Start Over?')
                    if ans:
                        self.reset_world('level1.txt')
                        self._level = 'level1.txt'
                        self._player.clear_score()
                        self._player.change_health(
                            self._player.get_max_health())
                        self.redraw_status()
                    else:
                        self._master.destroy()
                else:
                    self.reset_world(self.get_next_level())
                    self._level = self.get_next_level()
        elif block.get_id() == "tunnel":
            if get_collision_direction(
                    player, block) == "A" and self._player.is_duck() is True:
                self._player.set_duck(False)
                self.reset_world(self.get_next_level())
        elif block.get_id() == 'switches':
            if block.is_active():
                block.on_hit(arbiter, (self._world, player))

        block.on_hit(arbiter, (self._world, player))
        return True

    def _handle_player_collide_mob(self, player: Player, mob: Mob, data,
                                   arbiter: pymunk.Arbiter) -> bool:
        if player.is_niubi():
            self._world.remove_mob(mob)
        elif player.is_shoot():
            player.set_shoot(False)
        else:
            mob.on_hit(arbiter, (self._world, player))
        return True

    def _handle_player_separate_block(self, player: Player, block: Block, data,
                                      arbiter: pymunk.Arbiter) -> bool:
        return True
Exemple #10
0
class MarioApp :
    """High-level app class for Mario, a 2d platformer"""

    _world: World

    def __init__(self, master: tk.Tk) :
        """Construct a new game of a MarioApp game.

        Parameters:
            master (tk.Tk): tkinter root widget
        """
        self._master = master
        config_name = askopenfilename(filetypes=( ("Text file", "*.txt"),("HTML files", "*.html;*.htm")))
        self.load_config(config_name)
        self._gravity
        self._level_name
        self._healthdeterminant
        world_builder = WorldBuilder(BLOCK_SIZE, (0,300), fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder

        self._player = Player(max_health= self._health)
        self._powerup = PowerUp()
        self.reset_world(self._level_name)

        #self._renderer = MarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES, MOB_IMAGES)
        self._sprite_renderer = SpriteSheetView(BLOCK_IMAGES, ITEM_IMAGES, MOB_IMAGES)
        size = tuple(map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(master, size, self._sprite_renderer)
        self._view.pack()

        self.bind()

        # Wait for window to update before continuing
        master.update_idletasks()

        self._status_view = StatusView(master, self._health)
        self._status_view.pack()
        self._status_view.update_score(self._player.get_score())
        self._status_view.update_health(self._player.get_health())

        self._setup_collision_handlers()
        master.update_idletasks()
        self._start_time = 0
        self.step()
        self.menu = MenuBar(master, [("File", {"Reset" : self._reset,
                                               "Load Level" : self._import_new_level,
                                               "Exit" : self._close,
                                               "High Score": self._status_view.high})])
        self._collide = False

    def load_config(self,file_name):
        """
        load a configuration file, which contain the parameter of this game
        this file will define the new character ,level path, loading position,
        weight, health value and max_velocity.
        :param file_name:
        :return:
        """
        result = []
        f = open(file_name, "r")
        for line in f.readlines() :
            result.append(line.strip('\n'))

        for i in range(len(result)) :
            if result[i] == '==World==' :
                gravity_re = result[i + 1]
                gravity = re.findall(r'(\d+)', gravity_re)
                a = int(gravity[0])
                self._gravity = range(0,a)
                start_level = result[i + 2]
                level = start_level.split(' ')
                self._level_name = level[2]
            if result[i] == '==Player==' :
                character_re = result[i + 1]
                character = character_re.split(' ')
                b = character[2]
                x_re = result[i + 2]
                x = re.findall(r'(\d+)', x_re)
                x = int(x[0])
                y_re = result[i + 3]
                y = re.findall(r'(\d+)', y_re)
                y = int(y[0])
                mass_re = result[i + 4]
                mass = re.findall(r'(\d+)', mass_re)
                mass = int(mass[0])
                health_re = result[i + 5]
                health = re.findall(r'(\d+)', health_re)
                self._health = int(health[0])
                velocity_re = result[i + 6]
                velocity = re.findall(r'(\d+)', velocity_re)
                velocity = int(velocity[0])

    def reset_world(self, new_level: str):
        '''
        reset the current world, and bind the buttons again
        :param new_level:
        :return:
        '''
        self._world = load_world(self._builder, new_level)
        self._world.add_player(self._player, BLOCK_SIZE, BLOCK_SIZE)
        self._builder.clear()
        self.bind()

        self._setup_collision_handlers()

    def bind(self) :
        """Bind all the keyboard events to their event handlers."""
        self._master.bind("<space>", lambda a : self._jump())
        self._master.bind("<Up>", lambda a : self._jump())
        self._master.bind("w", lambda a : self._jump())
        self._master.bind("a", lambda a : self._move(-1, 0))
        self._master.bind("<Left>", lambda a : self._move(-1, 0))
        self._master.bind("d", lambda a : self._move(1, 0))
        self._master.bind("<Right>", lambda a : self._move(1, 0))
        self._master.bind("s", lambda a : self._duck())
        self._master.bind("<Down>", lambda a : self._duck())
        if self._powerup.get_powerup():
            self._master.bind("z", lambda a : self._powerup.fire((self._world, self._player)))

    def _import_new_level(self) :
        '''
        when the user click the load level entry in file menu, this widget will come out.
        :return:
        '''

        def load() :
            self._player.change_health(self._player.get_max_health() - self._player.get_health())
            self._player.change_score(-self._player.get_score())
            level_name = new_name.get()
            self.reset_world(level_name)
            Level_load_new.destroy()

        Level_load_new = tk.Toplevel()
        Level_load_new.geometry('400x100')
        Level_load_new.title('Load New Level')
        new_name = tk.StringVar()
        new_name.set('level1.txt')
        tk.Label(Level_load_new, text='Please enter the level name: ').place(x=10, y=10)
        entry_new_name = tk.Entry(Level_load_new, textvariable=new_name)
        entry_new_name.place(x=200, y=10)
        btn_comfirm = tk.Button(Level_load_new, text='OK', command=load)
        btn_comfirm.place(x=180, y=80)

    def _reset(self) :
        '''reset the current level'''
        self._player.change_health(self._player.get_max_health() - self._player.get_health())
        self._player.change_score(-self._player.get_score())
        self.reset_world(level_name)

    def _close(self) :
        """ Exit the drawing application """
        result = tk.messagebox.askquestion(title="Quiz Window", message="Do you really wanna quiz?")
        if (result == "yes") :
            self._master.destroy()

    def redraw(self) :
        """Redraw all the entities in the game canvas."""
        self._view.delete(tk.ALL)
        self._view.draw_entities(self._world.get_all_things())

    def scroll(self) :
        """Scroll the view along with the player in the center unless
        they are near the left or right boundaries
        """
        x_position = self._player.get_position()[0]
        half_screen = self._master.winfo_width() / 2
        world_size = self._world.get_pixel_size()[0] - half_screen

        # Left side
        if x_position <= half_screen :
            self._view.set_offset((0, 0))

        # Between left and right sides
        elif half_screen <= x_position <= world_size :
            self._view.set_offset((half_screen - x_position, 0))

        # Right side
        elif x_position >= world_size :
            self._view.set_offset((half_screen - world_size, 0))

    def step(self) :
        """Step the world physics and redraw the canvas."""
        data = (self._world, self._player)
        self._world.step(data)

        if self._player.get_health() == 0:
            result = tk.messagebox.askquestion(title="Quiz Window", message="Do you really wanna quiz?")
            if result == "yes":
                self._master.destroy()
            else:
                self._reset()
        self.scroll()
        self.redraw()
        self._master.after(10, self.step)
        self._status_view.update_score((self._player.get_score()))
        if time.time() - self._start_time >= 10 and self._player.get_invincible() is True :
            self._player.set_invincible(False)
        if self._player.get_invincible():
            health = self._player.get_health()
            ratio = health/self._health
            self._status_view._canvas.create_rectangle(0, 0, 1080*ratio, 20, fill="yellow")
            self._status_view._canvas.create_rectangle(1080 * ratio* 0.2, 0, 1080, 20, fill="black")
        else:
            self._status_view.update_health(self._player.get_health())

    def _move(self, dx, dy) :
        ''' make the mario move '''
        self._player.set_velocity((dx * 60, dy * 60))

    def _jump(self) :
        '''make the mario jump'''
        velocity = self._player.get_velocity()
        self._player.set_velocity((velocity.x, -120))

    def _duck(self) :
        '''make the mario duck'''
        velocity = self._player.get_velocity()
        self._player.set_velocity((velocity.x, velocity.y + 120))

    def _tunnel(self, block: Block):
        '''when the player stand on a tunnel and press the <down> or s, this function will bring him to a new level'''
        self.reset_world(new_level=block.get_filename())

    def _setup_collision_handlers(self) :
        self._world.add_collision_handler("player", "item", on_begin=self._handle_player_collide_item)
        self._world.add_collision_handler("player", "block", on_begin=self._handle_player_collide_block,
                                          on_separate=self._handle_player_separate_block)
        self._world.add_collision_handler("player", "mob", on_begin=self._handle_player_collide_mob)
        self._world.add_collision_handler("mob", "block", on_begin=self._handle_mob_collide_block)
        self._world.add_collision_handler("mob", "mob", on_begin=self._handle_mob_collide_mob)
        self._world.add_collision_handler("mob", "item", on_begin=self._handle_mob_collide_item)

    def _handle_mob_collide_block(self, mob: Mob, block: Block, data,
                                  arbiter: pymunk.Arbiter) -> bool :
        '''
        handle when the mob hit other block
        when the mob is fireball and hit a brick, the block will be destroyed
        when the mob is mushroom and hit a brick, it will change its direction
        '''
        if mob.get_id() == "fireball" :
            if block.get_id() == "brick" :
                self._world.remove_block(block)
            self._world.remove_mob(mob)
        if mob.get_id() == "mushroom":
            if get_collision_direction(mob, block) == "L" or get_collision_direction(mob, block) == "R" :
                mob.set_tempo(-mob.get_tempo())
        return True

    def _handle_mob_collide_item(self, mob: Mob, block: Block, data,
                                 arbiter: pymunk.Arbiter) -> bool :
        return False

    def _handle_mob_collide_mob(self, mob1: Mob, mob2: Mob, data,
                                arbiter: pymunk.Arbiter) -> bool :
        '''
        when the mushroom hit a fireball it will be destroyed and when he hit another mushroom it will
        change its direction
        '''
        if mob1.get_id() == "fireball" or mob2.get_id() == "fireball" :
            self._world.remove_mob(mob1)
            self._world.remove_mob(mob2)
        if mob1.get_velocity() == "mushroom" or mob2.get_id() == "mushroom" :
            mob1.set_tempo(-mob1.get_tempo())
            mob2.set_tempo(-mob2.get_tempo())

        return False

    def _handle_player_collide_item(self, player: Player, dropped_item: DroppedItem,
                                    data, arbiter: pymunk.Arbiter) -> bool :
        """Callback to handle collision between the player and a (dropped) item. If the player has sufficient space in
        their to pick up the item, the item will be removed from the game world.

        Parameters:
            player (Player): The player that was involved in the collision
            dropped_item (DroppedItem): The (dropped) item that the player collided with
            data (dict): data that was added with this collision handler (see data parameter in
                         World.add_collision_handler)
            arbiter (pymunk.Arbiter): Data about a collision
                                      (see http://www.pymunk.org/en/latest/pymunk.html#pymunk.Arbiter)
                                      NOTE: you probably won't need this
        Return:
             bool: False (always ignore this type of collision)
                   (more generally, collision callbacks return True iff the collision should be considered valid; i.e.
                   returning False makes the world ignore the collision)
        """

        dropped_item.collect(self._player)
        self._world.remove_item(dropped_item)
        if dropped_item.get_id() == "star" :
            self._player.set_invincible(True)
            self._start_time = time.time()
        if dropped_item.get_id() == "flower":
            self._powerup.set_powerup(True)
        return False

    def _handle_player_collide_block(self, player: Player, block: Block, data,
                                     arbiter: pymunk.Arbiter) -> bool :
        ''' handle when the player hit some blocks, when they are colliding, the on_hit function will run'''
        if block.get_id() == "switch":
            block.on_hit(arbiter, (self._world, player))
            return False
        if block.get_id() == "flag":
            if get_collision_direction(player, block) == "L" or get_collision_direction(player, block) == "R":
                self.reset_world(new_level=block.get_filename())
            if get_collision_direction(player, block) == "A" :
                block.on_hit(arbiter, (self._world, player))
        if block.get_id() == "tunnel":
            if get_collision_direction(player, block) == "A":
                #
                self._master.bind("s", lambda a : self._tunnel(block))
                self._master.bind("<Down>", lambda a : self._tunnel(block))
        else:
            block.on_hit(arbiter, (self._world, player))
        return True

    def _handle_player_collide_mob(self, player: Player, mob: Mob, data,
                                   arbiter: pymunk.Arbiter) -> bool :
        ''' handle when the player hit some mob, when they are colliding, the on_hit function will run'''
        if mob.get_id() == "mushroom" :
            if get_collision_direction(player, mob) == "L" or get_collision_direction(player, mob) == "R" :
                if self._player.get_invincible() :
                    self._world.remove_mob(mob)
                elif self._powerup.get_powerup():
                    self._powerup.set_powerup(False)
                else :
                    mob.on_hit(arbiter, (self._world, player))
                    mob.set_tempo(-mob.get_tempo())
            if get_collision_direction(player, mob) == "A":
                mob.on_hit(arbiter, (self._world, player))
        if mob.get_id() == "fireball" :
            if self._player.get_invincible() :
                self._world.remove_mob(mob)
            elif self._powerup.get_powerup():
                self._powerup.set_powerup(False)
            else :
                mob.on_hit(arbiter, (self._world, player))
                self._world.remove_mob(mob)
        return True

    def _handle_player_separate_block(self, player: Player, block: Block, data,
                                      arbiter: pymunk.Arbiter) -> bool :
        return True
Exemple #11
0
class MarioApp:
    """High-level app class for Mario, a 2d platformer"""

    _world: World

    def __init__(self, master: tk.Tk):
        """Construct a new game of a MarioApp game.

        Parameters:
            master (tk.Tk): tkinter root widget
        """
        self._master = master
        self._master.title("Mario bird")

        world_builder = WorldBuilder(BLOCK_SIZE, gravity=(0, 300), fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder

        self._player = Player(max_health=5)
        self.reset_world('level1.txt')
        self._level_holder = 'level1.txt'

        self._renderer = MarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES, MOB_IMAGES)

        size = tuple(map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(master, size, self._renderer)
        self._view.pack()
        self.bind()
        self.menubar()
        #Stats controller for changing the status bar.
        #used in the function 'step'
        self._health = self._player.get_health()
        self._score = self._player.get_score()
        self._status_bar = tk.Frame(self._master)
        self.status_bar()
        # Wait for window to update before continuing
        master.update_idletasks()
        self.step()
        self._death_action = False

    def reset_world(self, new_level):
        self._world = load_world(self._builder, new_level)
        self._world.add_player(self._player, BLOCK_SIZE, BLOCK_SIZE)
        self._builder.clear()
        self._setup_collision_handlers()
        self._level_holder = new_level


        self._death_action = False
        self._player.change_health(self._player.get_max_health())

    def menubar(self):
        """Creates a menu bar in GUI interface with options:
                Load level: prompts user to select level to load
                Reset level: Resets current level
                Exit: exits game
        """
        menubar = tk.Menu(self._master)
        self._master.config(menu=menubar)
        filemenu = tk.Menu(menubar)
        menubar.add_cascade(label="File", menu=filemenu)
        filemenu.add_command(label="LOAD LEVEL", command=self.load_level_menu)
        filemenu.add_command(label="RESET LEVEL", command=lambda: self.reset_world(self._level_holder))
        filemenu.add_command(label="EXIT", command=self.exit_game)

    def load_level_menu(self):
        """Pop-up window with 3 buttons
            Level1: loads level1
            level2: loads level2
        """
        popup = tk.Tk()
        popup.title("Level select")
        label1 = tk.Label(popup, text="Choose a level")
        label1.pack(side='top')
        level1_button = tk.Button(popup, text="Level1", command=lambda: [self.reset_world('level1.txt'),popup.destroy()])
        level2_button = tk.Button(popup, text="level2", command=lambda: [self.reset_world('level2.txt'),popup.destroy()])
        level1_button.pack(side='left', expand=True)
        level2_button.pack(side='right', expand=True)

    def exit_game(self):
        self._master.destroy()

    def bind(self):
        """Bind all the keyboard events to their event handlers."""
        # jump
        self._master.bind('<Up>', lambda event: self._jump())
        self._master.bind('<space>', lambda event: self._jump())
        self._master.bind('<w>', lambda event: self._jump())
        self._master.bind('<W>', lambda event: self._jump())
        # move left
        self._master.bind('<a>', lambda event: self._move(-50, 0))
        self._master.bind('<Left>', lambda event: self._move(-500, 0))
        self._master.bind('<A>', lambda event: self._move(-500, 0))
        # duck
        self._master.bind('<s>', lambda event: self._duck())
        self._master.bind('<Down>', lambda event: self._duck())
        self._master.bind('<S>', lambda event: self._duck())
        # move right
        self._master.bind('<d>', lambda event: self._move(50, 0))
        self._master.bind('<Right>', lambda event: self._move(500, 0))
        self._master.bind('<D>', lambda event: self._move(500, 0))

    def status_bar(self):
        self._status_bar.destroy()
        self._status_bar = tk.Frame(self._master)
        self._status_bar.pack(side = tk.BOTTOM)
        healthbar_canvas = tk.Canvas(self._status_bar, width=1080, height=25, bg='black')
        healthbar_canvas.pack(side="top")
        healthbar_width = (self._player.get_health() / self._player.get_max_health()) * 1080
        # set healthbar color
        color_controller = healthbar_width / 1080
        if color_controller >= 0.50:
            color = 'Green'
        elif 0.25 <= color_controller <= 0.50:
            color = 'Orange'
        elif color_controller < 0.25:
            color = 'Red'
        # Healthbar= shape:Rectangle
        healthbar_canvas.create_rectangle(0, 25, healthbar_width, 0, fill=color)
        # label to display score
        w = tk.Label(self._status_bar, text='Score: ' + str(self._player.get_score()))
        w.pack(side="bottom")

    def redraw(self):
        """Redraw all the entities in the game canvas."""
        self._view.delete(tk.ALL)

        self._view.draw_entities(self._world.get_all_things())

    def scroll(self):
        """Scroll the view along with the player in the center unless
        they are near the left or right boundaries
        """
        x_position = self._player.get_position()[0]
        half_screen = self._master.winfo_width() / 2
        world_size = self._world.get_pixel_size()[0] - half_screen

        # Left side
        if x_position <= half_screen:
            self._view.set_offset((0, 0))

        # Between left and right sides
        elif half_screen <= x_position <= world_size:
            self._view.set_offset((half_screen - x_position, 0))

        # Right side
        elif x_position >= world_size:
            self._view.set_offset((half_screen - world_size, 0))

    def step(self):
        """Step the world physics and redraw the canvas."""
        """SMOOTH OPERATOR ..... SMOOOOTH OPERATION"""
        data = (self._world, self._player)
        self._world.step(data)
        self.scroll()
        self.redraw()
        self._master.after(10, self.step)
        #updates the status bar if change in health is detected
        if self._player.get_health() == self._health:
            pass
        else:
            self._health = self._player.get_health()
            self.status_bar()
        #updates the status bar if change in score is detected
        if self._player.get_score() == self._score:
            pass
        else:
            self._score = self._player.get_score()
            self.status_bar()

        #Asking if players want to continue playing or end the game
        if self._player.get_health() == 0:
            if not self._death_action:
                self.on_death()
            else:
                pass

    def on_death(self):
        """
        A popup window asking if player wants to continue or exit
        """
        self._death_action = True
        death_popup = tk.Tk()
        death_popup.geometry("300x200")
        death_popup.configure(background="light blue")
        death_popup.title("You dead")
        label1 = tk.Label(death_popup, text="What next??")
        label1.pack(side='top')
        restart_button = tk.Button(death_popup, text="Restart", command=lambda: [self.reset_world('level1.txt'), death_popup.destroy()])
        end_button = tk.Button(death_popup, text='end', command=lambda: [self.exit_game(), death_popup.destroy()])
        restart_button.pack(side='left', expand=True)
        end_button.pack(side='right', expand=True)



    def _move(self, dx, dy):
        self._player.set_velocity((dx, dy))

    def _jump(self):
        self._player.set_velocity((0, -150))

    def _duck(self):
        # to be used later as if duck == True
        return True

    def _setup_collision_handlers(self):
        self._world.add_collision_handler("player", "item", on_begin=self._handle_player_collide_item)
        self._world.add_collision_handler("player", "block", on_begin=self._handle_player_collide_block,
                                          on_separate=self._handle_player_separate_block)
        self._world.add_collision_handler("player", "mob", on_begin=self._handle_player_collide_mob)
        self._world.add_collision_handler("mob", "block", on_begin=self._handle_mob_collide_block)
        self._world.add_collision_handler("mob", "mob", on_begin=self._handle_mob_collide_mob)
        self._world.add_collision_handler("mob", "item", on_begin=self._handle_mob_collide_item)

    def _handle_mob_collide_block(self, mob: Mob, block: Block, data,
                                  arbiter: pymunk.Arbiter) -> bool:
        if mob.get_id() == "fireball":
            if block.get_id() == "brick":
                self._world.remove_block(block)
            self._world.remove_mob(mob)
        return True

    def _handle_mob_collide_item(self, mob: Mob, block: Block, data,
                                 arbiter: pymunk.Arbiter) -> bool:
        return False

    def _handle_mob_collide_mob(self, mob1: Mob, mob2: Mob, data,
                                arbiter: pymunk.Arbiter) -> bool:
        if mob1.get_id() == "fireball" or mob2.get_id() == "fireball":
            self._world.remove_mob(mob1)
            self._world.remove_mob(mob2)

        return False

    def _handle_player_collide_item(self, player: Player, dropped_item: DroppedItem,
                                    data, arbiter: pymunk.Arbiter) -> bool:
        """Callback to handle collision between the player and a (dropped) item. If the player has sufficient space in
        their to pick up the item, the item will be removed from the game world.

        Parameters:
            player (Player): The player that was involved in the collision
            dropped_item (DroppedItem): The (dropped) item that the player collided with
            data (dict): data that was added with this collision handler (see data parameter in
                         World.add_collision_handler)
            arbiter (pymunk.Arbiter): Data about a collision
                                      (see http://www.pymunk.org/en/latest/pymunk.html#pymunk.Arbiter)
                                      NOTE: you probably won't need this
        Return:
             bool: False (always ignore this type of collision)
                   (more generally, collision callbacks return True iff the collision should be considered valid; i.e.
                   returning False makes the world ignore the collision)
        """

        dropped_item.collect(self._player)
        self._world.remove_item(dropped_item)
        return False

    def _handle_player_collide_block(self, player: Player, block: Block, data,
                                     arbiter: pymunk.Arbiter) -> bool:

        block.on_hit(arbiter, (self._world, player))
        return True

    def _handle_player_collide_mob(self, player: Player, mob: Mob, data,
                                   arbiter: pymunk.Arbiter) -> bool:
        mob.on_hit(arbiter, (self._world, player))
        return True

    def _handle_player_separate_block(self, player: Player, block: Block, data,
                                      arbiter: pymunk.Arbiter) -> bool:
        return True
Exemple #12
0
    def __init__(self, master: tk.Tk):
        """Construct a new game of a MarioApp game.

        Parameters:
            master (tk.Tk): tkinter root widget
        """
        """
        -------------------------------------
        task 2.3 
        增加函数 load_config(在MarioApp)最底部
        涉及到的变量参数修改已在注释中标出
        -------------------------------------
        """
        self._master = master
        # 设置了一些参数默认值
        self.gravity = 300  # world_builder = WorldBuilder(BLOCK_SIZE, gravity=(0,self.gravity), fallback=create_unknown)
        self.level = 'level1.txt'  # 直接改变level值
        self.character_name = 'mario'  # self._player = Player(max_health=5,name=self.character_name)
        self.character_x = 1  # 在add_player中改变参数
        self.character_y = 1  # 在add_player中改变参数
        self.character_mass = 100  # 在add_player中改变参数
        self.max_velocity = 200  # 暂时不知参数位置
        # 调用load config函数
        self._load_config()
        #----------------------------------------------------------task3

        fm = tk.Frame(self._master)
        fm.pack(padx=10, expand=1)
        load = tk.Button(fm, text='Load Level', command=self.load_f)
        load.pack(side=RIGHT, fill=Y)
        rest = tk.Button(fm, text='Rest Level', command=self.rest_f)
        rest.pack(side=RIGHT, fill=Y)
        Exit = tk.Button(fm, text='Exits the game', command=self._master.quit)
        Exit.pack(side=RIGHT, fill=Y)

        world_builder = WorldBuilder(BLOCK_SIZE,
                                     gravity=(0, self.gravity),
                                     fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder

        self._player = Player(max_health=5, name=self.character_name)
        self.reset_world(self.level)

        self._renderer = MarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES,
                                           MOB_IMAGES)

        size = tuple(
            map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(master, size, self._renderer)
        self._view.pack()

        self.bind()

        #``````````````````````````````````````````````````
        #task1.3
        Health = tk.Frame(self._master)
        Health.pack(padx=300, pady=50)
        self.score_num = tk.Variable = 0
        self.Score = tk.Button(Health, text='Score:' + str(self.score_num))
        self.Score.pack(side=RIGHT, fill=Y)
        label = tk.Label(bg="black", width=self._player._max_health)
        label.place(relx=0, rely=0.9)
        self.label1 = tk.Label(bg="blue", width=self._player._health)
        self.label1.place(relx=0, rely=0.9)
        #``````````````````````````````````````````````````

        # Wait for window to update before continuing
        master.update_idletasks()
        self.step()
        self._master.mainloop()
Exemple #13
0
class MarioApp:
    """High-level app class for Mario, a 2d platformer"""

    _world: World

    def __init__(self, master: tk.Tk):
        """Construct a new game of a MarioApp game.

        Parameters:
            master (tk.Tk): tkinter root widget
        """
        """
        -------------------------------------
        task 2.3 
        增加函数 load_config(在MarioApp)最底部
        涉及到的变量参数修改已在注释中标出
        -------------------------------------
        """
        self._master = master
        # 设置了一些参数默认值
        self.gravity = 300  # world_builder = WorldBuilder(BLOCK_SIZE, gravity=(0,self.gravity), fallback=create_unknown)
        self.level = 'level1.txt'  # 直接改变level值
        self.character_name = 'mario'  # self._player = Player(max_health=5,name=self.character_name)
        self.character_x = 1  # 在add_player中改变参数
        self.character_y = 1  # 在add_player中改变参数
        self.character_mass = 100  # 在add_player中改变参数
        self.max_velocity = 200  # 暂时不知参数位置
        # 调用load config函数
        self._load_config()
        #----------------------------------------------------------task3

        fm = tk.Frame(self._master)
        fm.pack(padx=10, expand=1)
        load = tk.Button(fm, text='Load Level', command=self.load_f)
        load.pack(side=RIGHT, fill=Y)
        rest = tk.Button(fm, text='Rest Level', command=self.rest_f)
        rest.pack(side=RIGHT, fill=Y)
        Exit = tk.Button(fm, text='Exits the game', command=self._master.quit)
        Exit.pack(side=RIGHT, fill=Y)

        world_builder = WorldBuilder(BLOCK_SIZE,
                                     gravity=(0, self.gravity),
                                     fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder

        self._player = Player(max_health=5, name=self.character_name)
        self.reset_world(self.level)

        self._renderer = MarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES,
                                           MOB_IMAGES)

        size = tuple(
            map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(master, size, self._renderer)
        self._view.pack()

        self.bind()

        #``````````````````````````````````````````````````
        #task1.3
        Health = tk.Frame(self._master)
        Health.pack(padx=300, pady=50)
        self.score_num = tk.Variable = 0
        self.Score = tk.Button(Health, text='Score:' + str(self.score_num))
        self.Score.pack(side=RIGHT, fill=Y)
        label = tk.Label(bg="black", width=self._player._max_health)
        label.place(relx=0, rely=0.9)
        self.label1 = tk.Label(bg="blue", width=self._player._health)
        self.label1.place(relx=0, rely=0.9)
        #``````````````````````````````````````````````````

        # Wait for window to update before continuing
        master.update_idletasks()
        self.step()
        self._master.mainloop()

    def re_health(self):
        if (self._player._health >= self._player._max_health * 0.5):
            #print(self.a)
            self.label1.config(width=self._player._health, bg='green')
        elif (self._player._health <= self._player._max_health * 0.25):
            self.label1.config(width=self._player._health, bg='red')
        else:
            self.label1.config(width=self._player._health, bg='orange')

    def re_score(self):
        self.Score.config(text='Score:' + str(self.score_num))

    def rest_f(self):
        if (self.level == 'level1.txt'):
            self.reset_world('level1.txt')
        else:
            self.reset_world('level2.txt')

    def load_f(self):
        def read():
            text = xls.get()
            level_num = int(text)
            print(level_num)
            if (level_num == 1):
                self.reset_world('level1.txt')
                self.level = 'level1.txt'
            else:
                self.reset_world('level2.txt')
                self.level = 'level2.txt'
            root1.destroy()

        root1 = tk.Tk()
        root1.title("load")
        l1 = tk.Label(root1, text="plz input level(1 or 2)")
        l1.pack()  # 这里的side可以赋值为LEFT  RTGHT TOP  BOTTOM
        xls = tk.Entry(root1)
        xls.pack()
        tk.Button(root1, text='finish', command=read).pack()
        root1.mainloop()

    def reset_world(self, new_level):
        self._world = load_world(self._builder, new_level)
        self._world.add_player(self._player,
                               self.character_x * BLOCK_SIZE,
                               self.character_y * BLOCK_SIZE,
                               mass=self.character_mass)
        self._builder.clear()
        self.level = new_level
        self._setup_collision_handlers()

    def bind(self):
        """Bind all the keyboard events to their event handlers."""
        self._master.bind_all("<KeyPress>", self._handle)
        pass

    def redraw(self):
        """Redraw all the entities in the game canvas."""
        self._view.delete(tk.ALL)

        self._view.draw_entities(self._world.get_all_things())

    def scroll(self):
        """Scroll the view along with the player in the center unless
        they are near the left or right boundaries
        """
        x_position = self._player.get_position()[0]
        half_screen = self._master.winfo_width() / 2
        world_size = self._world.get_pixel_size()[0] - half_screen

        # Left side
        if x_position <= half_screen:
            self._view.set_offset((0, 0))

        # Between left and right sides
        elif half_screen <= x_position <= world_size:
            self._view.set_offset((half_screen - x_position, 0))

        # Right side
        elif x_position >= world_size:
            self._view.set_offset((half_screen - world_size, 0))

    def step(self):
        """Step the world physics and redraw the canvas."""
        data = (self._world, self._player)
        self._world.step(data)

        self.scroll()
        self.redraw()

        self._master.after(10, self.step)

    def _handle(self, event):
        if (event.keysym == 'Up' or event.keysym == 'w'
                or event.keysym == 'space'):
            self._jump()
        elif (event.keysym == 'd' or event.keysym == 'Right'):
            self._move(70, 0)
        elif (event.keysym == 'a' or event.keysym == 'Left'):
            self._move(-70, 0)
        elif (event.keysym == 's' or event.keysym == 'Down'):
            self._duck()
        else:
            pass

    def _move(self, dx, dy):
        self._player.set_velocity((dx, dy))
        print("move")
        pass

    def _jump(self):
        print(self._player.set_jumping(True))
        print("jump")
        pass

    def _duck(self):
        print("duck")
        pass

    def _setup_collision_handlers(self):
        self._world.add_collision_handler(
            "player", "item", on_begin=self._handle_player_collide_item)
        self._world.add_collision_handler(
            "player",
            "block",
            on_begin=self._handle_player_collide_block,
            on_separate=self._handle_player_separate_block)
        self._world.add_collision_handler(
            "player", "mob", on_begin=self._handle_player_collide_mob)
        self._world.add_collision_handler(
            "mob", "block", on_begin=self._handle_mob_collide_block)
        self._world.add_collision_handler(
            "mob", "mob", on_begin=self._handle_mob_collide_mob)
        self._world.add_collision_handler(
            "mob", "item", on_begin=self._handle_mob_collide_item)

    def _handle_mob_collide_block(self, mob: Mob, block: Block, data,
                                  arbiter: pymunk.Arbiter) -> bool:
        if mob.get_id() == "fireball":
            if block.get_id() == "brick":
                self._world.remove_block(block)
            self._world.remove_mob(mob)
        return True

    def _handle_mob_collide_item(self, mob: Mob, block: Block, data,
                                 arbiter: pymunk.Arbiter) -> bool:
        return False

    def _handle_mob_collide_mob(self, mob1: Mob, mob2: Mob, data,
                                arbiter: pymunk.Arbiter) -> bool:
        if mob1.get_id() == "fireball" or mob2.get_id() == "fireball":
            self._world.remove_mob(mob1)
            self._world.remove_mob(mob2)

        return False

    def _handle_player_collide_item(self, player: Player,
                                    dropped_item: DroppedItem, data,
                                    arbiter: pymunk.Arbiter) -> bool:
        """Callback to handle collision between the player and a (dropped) item. If the player has sufficient space in
        their to pick up the item, the item will be removed from the game world.

        Parameters:
            player (Player): The player that was involved in the collision
            dropped_item (DroppedItem): The (dropped) item that the player collided with
            data (dict): data that was added with this collision handler (see data parameter in
                         World.add_collision_handler)
            arbiter (pymunk.Arbiter): Data about a collision
                                      (see http://www.pymunk.org/en/latest/pymunk.html#pymunk.Arbiter)
                                      NOTE: you probably won't need this
        Return:
             bool: False (always ignore this type of collision)
                   (more generally, collision callbacks return True iff the collision should be considered valid; i.e.
                   returning False makes the world ignore the collision)
        """

        dropped_item.collect(self._player)
        self._world.remove_item(dropped_item)
        return False

    def _handle_player_collide_block(self, player: Player, block: Block, data,
                                     arbiter: pymunk.Arbiter) -> bool:

        block.on_hit(arbiter, (self._world, player))
        return True

    def _handle_player_collide_mob(self, player: Player, mob: Mob, data,
                                   arbiter: pymunk.Arbiter) -> bool:
        mob.on_hit(arbiter, (self._world, player))
        return True

    def _handle_player_separate_block(self, player: Player, block: Block, data,
                                      arbiter: pymunk.Arbiter) -> bool:
        return True

    def _load_config(self):
        try:
            with open('config.txt') as f:
                t = f.read()
                things = re.findall(r'(.*) : (.*)', t)
                print(things)
                for i, k in things:
                    if (i == 'gravity'):
                        self.gravity = int(k)
                    if (i == "start"):
                        self.level = k
                    if (i == 'character'):
                        self.character_name = k
                    if (i == 'x'):
                        self.character_x = int(k)
                    if (i == 'y'):
                        self.character_y = int(k)
                    if (i == 'mass'):
                        self.character_mass = int(k)
                    if (i == 'max_velocity'):
                        self.max_velocity = int(k)
        except:
            newTk = tk.Tk()
            ErrorWindow = tk.Frame(newTk)
            newTk.geometry('1080x500')
            ErrorWindow.pack()
            tk.Label(
                newTk,
                text='load config error\n please check the config.txt').pack(
                    padx=200, pady=200)
            self._master.destroy()
            newTk.mainloop()
Exemple #14
0
class MarioApp:
    """High-level app class for Mario, a 2d platformer"""
    def __init__(self, master):
        """Construct a new game of a MarioApp game.

        Parameters:
            master (tk.Tk): tkinter root widget
        """
        self._master = master

        self._start_game()

        def donothing():
            filewin = tk.Toplevel(self._master)
            button = tk.Button(filewin, text="Do nothing button")
            button.pack()

        menubar = tk.Menu(self._master)
        filemenu = tk.Menu(menubar, tearoff=0)
        filemenu.add_command(label="Load Level", command=self._retrieve_input)
        filemenu.add_command(label="Reset Level",
                             command=lambda: self.reset_world(full=True))
        filemenu.add_command(label="High Scores",
                             command=self.display_high_scores)
        filemenu.add_command(label="Exit", command=self._ask_quit)
        menubar.add_cascade(label="File", menu=filemenu)
        self._master.config(menu=menubar)

        # Special Item: Star
        self._player_star = PlayerStar()

        # Special Block: switch
        self._switches = []
        for i in self._world.get_all_things():
            try:
                if i._id == "switch":
                    self._switches.append(i)
            except:
                pass

        self._can_jump = False

        # Score
        self._scores = HighScores()

        # Wait for window to update before continuing
        master.update_idletasks()
        self.step()

    def _start_game(self):
        path = tk.simpledialog.askstring("Mario", "Configuration file:")
        self._game = loader.load_level(path)
        if self._game == None:
            tk.messagebox.showerror("Error", "Configuration file wrong.")
            self._master.destroy()

        # World builder
        world_builder = WorldBuilder(
            BLOCK_SIZE,
            gravity=(0, int(self._game["==World=="]["gravity"])),
            fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder

        self._current_level = self._game["==World=="]['start']
        self._world = load_world(self._builder, self._current_level)

        # Set tunnel/goal
        Flagpole._next_level = self._game[("==" + self._current_level +
                                           "==")]["goal"]
        Tunnel._next_level = self._game[("==" + self._current_level +
                                         "==")].get("tunnel", None)

        self._player = Player(
            max_health=int(self._game["==Player=="]["health"]))
        print(self._player.get_shape())
        self._world.add_player(self._player,
                               int(self._game["==Player=="]["x"]),
                               int(self._game["==Player=="]["y"]),
                               int(self._game["==Player=="]["mass"]))

        self._max_speed = int(self._game["==Player=="]["max_velocity"])

        self._setup_collision_handlers()

        self._renderer = AnimatedMarioViewRenderer(BLOCK_IMAGES, ITEM_IMAGES,
                                                   MOB_IMAGES)  # Into Animated

        size = tuple(
            map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(self._master, size, self._renderer)
        self._view.pack()

        self._status_view = StatusView(self._master,
                                       int(self._game["==Player=="]['health']),
                                       0)

        self.bind()

    def reset_world(self, new_level=None, full=False):
        if new_level == None:
            new_level = self._current_level
        else:
            self._current_level = new_level
            self._scores = HighScores()
        world_builder = WorldBuilder(BLOCK_SIZE,
                                     gravity=(0, 500),
                                     fallback=create_unknown)
        world_builder.register_builders(BLOCKS.keys(), create_block)
        world_builder.register_builders(ITEMS.keys(), create_item)
        world_builder.register_builders(MOBS.keys(), create_mob)
        self._builder = world_builder
        self._world = load_world(self._builder, new_level)

        Flagpole._next_level = self._game[("==" + self._current_level +
                                           "==")]["goal"]
        Tunnel._next_level = self._game[("==" + self._current_level +
                                         "==")].get("tunnel", None)

        if full:  # full reset player stats
            self._player = Player(
                max_health=int(self._game["==Player=="]["health"]))
        self._world.add_player(self._player,
                               int(self._game["==Player=="]["x"]),
                               int(self._game["==Player=="]["y"]),
                               int(self._game["==Player=="]["mass"]))

        self._setup_collision_handlers()

        self._view.pack_forget()
        size = tuple(
            map(min, zip(MAX_WINDOW_SIZE, self._world.get_pixel_size())))
        self._view = GameView(self._master, size, self._renderer)
        self._view.pack()

        self._status_view.unpack()
        self._status_view = StatusView(self._master,
                                       int(self._game["==Player=="]['health']),
                                       self._player._score)

    def display_high_scores(self):
        print(self._current_level,
              self._current_level.replace(".txt", "_score.txt"))
        top = tk.Toplevel()
        top.geometry("240x260")

        label = tk.Label(top, text="High Scores:")
        label.pack()

        if self._scores.load_scores(
                self._current_level.replace(".txt", "_score.txt")) == None:
            tk.Label(top, text="No high scores yet").pack()
            return
        for i in range(len(self._scores.get_top_10())):
            top10 = self._scores.get_top_10()
            tk.Label(top,
                     text=(str(i + 1) + ': ' + str(top10[i][0]) + '\t\t' +
                           str(top10[i][1]))).pack()

    def bind(self):
        """Bind all the keyboard events to their event handlers."""
        self._master.bind("a", lambda e: self._move(-1, 0))
        self._master.bind("<Left>", lambda e: self._move(-1, 0))
        self._master.bind("d", lambda e: self._move(1, 0))
        self._master.bind("<Right>", lambda e: self._move(1, 0))
        self._master.bind("w", lambda e: self._jump())
        self._master.bind("<Up>", lambda e: self._jump())
        self._master.bind("<space>", lambda e: self._jump())
        self._master.bind("s", lambda e: self._duck())
        self._master.bind("<Down>", lambda e: self._duck())

    def redraw(self):
        """Redraw all the entities in the game canvas."""
        self._view.delete(tk.ALL)
        self._view.draw_entities(self._world.get_all_things())

    def scroll(self):
        """Scroll the view along if the player is within SCROLL_RADIUS
        from the edge of the screen.
        """
        # calculate the x distance from the right edge
        x_position = self._player.get_position()[0]
        x_offset = self._view.get_offset()[0]
        screen_size = self._master.winfo_width()
        edge_distance = screen_size - (x_position + x_offset)

        if edge_distance < SCROLL_RADIUS:
            x_position -= 5

            # place a backstop boundary wall on the left side of the screen
            # to prevent the player from going backwards in the game
            world_space = self._world.get_space()
            wall = BoundaryWall("backstop", world_space.static_body,
                                (x_position, 0),
                                (x_position, self._world.get_pixel_size()[1]),
                                5)
            world_space.add(wall.get_shape())

            # shift the view offset by the screen size
            self._view.shift((-(screen_size - SCROLL_RADIUS), 0))

    def step(self):
        """Step the world physics and redraw the canvas."""
        data = (self._world, self._player)
        self._world.step(data)

        self.scroll()
        self.redraw()

        self._player_star.tick()
        if not self._player_star.activated():
            self._status_view.set_health(self._player.get_health())
        for i in self._switches:
            i.tick()
        self._master.after(10, self.step)

    def _move(self, dx, dy):
        if dx < 0:
            self._renderer.set_player_facing(-1)
        elif dx > 0:
            self._renderer.set_player_facing(1)
        velocity = self._player.get_velocity()
        sign = lambda x: 1 if x >= 0 else -1
        self._player.set_velocity((self._max_speed * sign(dx), velocity.y))

    def _jump(self):
        x, y = self._player.get_position()
        y += BLOCK_SIZE
        if self._world.get_block(x, y) != None:
            self._can_jump = True

        if self._can_jump:
            velocity = self._player.get_velocity()
            self._player.set_velocity((velocity.x * 0.8, -200))
            self._can_jump = False

    def _duck(self):
        x, y = self._player.get_position()
        y += BLOCK_SIZE
        if self._world.get_block(x, y) != None:
            if self._world.get_block(x, y)._id == "tunnel":
                if self._world.get_block(x, y)._next_level != None:
                    self.reset_world(self._world.get_block(x, y)._next_level)

    def _setup_collision_handlers(self):
        self._world.add_collision_handler(
            "player", "item", on_begin=self._handle_player_collide_item)
        self._world.add_collision_handler(
            "player",
            "block",
            on_begin=self._handle_player_collide_block,
            on_separate=self._handle_player_separate_block)
        self._world.add_collision_handler(
            "player", "mob", on_begin=self._handle_player_collide_mob)
        self._world.add_collision_handler(
            "mob", "block", on_begin=self._handle_mob_collide_block)
        self._world.add_collision_handler(
            "mob", "mob", on_begin=self._handle_mob_collide_mob)
        self._world.add_collision_handler(
            "mob", "item", on_begin=self._handle_mob_collide_item)

    def _handle_mob_collide_block(self, mob: Mob, block: Block, data,
                                  arbiter: pymunk.Arbiter) -> bool:
        if mob.get_id() == "fireball":
            if block.get_id() == "brick":
                self._world.remove_block(block)
            self._world.remove_mob(mob)
        if mob.get_id() == "mushroom":
            if get_collision_direction(block, mob) in ("L", "R"):
                mob.direction *= -1
        return True

    def _handle_mob_collide_item(self, mob: Mob, block: Block, data,
                                 arbiter: pymunk.Arbiter) -> bool:
        return False

    def _handle_mob_collide_mob(self, mob1: Mob, mob2: Mob, data,
                                arbiter: pymunk.Arbiter) -> bool:
        if mob1.get_id() == "fireball" or mob2.get_id() == "fireball":
            self._world.remove_mob(mob1)
            self._world.remove_mob(mob2)

        return False

    def _handle_player_collide_item(self, player: Player,
                                    dropped_item: DroppedItem, data,
                                    arbiter: pymunk.Arbiter) -> bool:
        """Callback to handle collision between the player and a (dropped) item. If the player has sufficient space in
        their to pick up the item, the item will be removed from the game world.

        Parameters:
            player (Player): The player that was involved in the collision
            dropped_item (DroppedItem): The (dropped) item that the player collided with
            data (dict): data that was added with this collision handler (see data parameter in
                         World.add_collision_handler)
            arbiter (pymunk.Arbiter): Data about a collision
                                      (see http://www.pymunk.org/en/latest/pymunk.html#pymunk.Arbiter)
                                      NOTE: you probably won't need this
        Return:
             bool: False (always ignore this type of collision)
                   (more generally, collision callbacks return True iff the collision should be considered valid; i.e.
                   returning False makes the world ignore the collision)
        """
        dropped_item.collect(self._player)

        if dropped_item.get_id() == "star":
            self._player_star.activate()
            self._status_view.set_health_colour("yellow")

        self._world.remove_item(dropped_item)
        self._status_view.set_score(self._player.get_score())
        return False

    def _handle_player_collide_block(self, player: Player, block: Block, data,
                                     arbiter: pymunk.Arbiter) -> bool:
        if get_collision_direction(player, block) == "A":
            self._can_jump = True

        if block._id == "bouncy":
            if get_collision_direction(player, block) == "A":
                self._renderer.activate_bouncy(block.get_shape())
        elif block._id == "switch":
            if block.activated():
                return False
        elif block._id == "flagpole":
            name = self._ask_name()
            score_name = self._current_level.replace(".txt", "_score.txt")
            self._scores.load_scores(score_name)
            self._scores.add_score(name, self._player.get_score())
            self._scores.save_scores(score_name)
            self.reset_world(block._next_level)
        block.on_hit(arbiter, (self._world, player))
        return True

    def _handle_player_collide_mob(self, player: Player, mob: Mob, data,
                                   arbiter: pymunk.Arbiter) -> bool:

        if self._player_star.activated():
            self._world.remove_mob(mob)
            return False
        else:
            if mob._id == "mushroom":
                if mob.dead:
                    return False
                if get_collision_direction(player, mob) == "A":
                    self._renderer.kill_mushroom(mob.get_shape())
                    mob.freeze()
                    self._master.after(500,
                                       lambda: self._world.remove_mob(mob))
                    self._can_jump = True
                    self._jump()
                    return True

            mob.on_hit(arbiter, (self._world, player))
            self._status_view.set_health(self._player.get_health())
            self._status_view.set_score(self._player.get_score())

            if self._player.get_health() == 0:
                tk.messagebox.showinfo("Gameover", "You died.")
                self._master.quit()
            return True

    def _handle_player_separate_block(self, player: Player, block: Block, data,
                                      arbiter: pymunk.Arbiter) -> bool:
        return True

    def _retrieve_input(self):
        level = tk.simpledialog.askstring("Load Level",
                                          "Please input the level name:")
        if level:
            self.reset_world(new_level=level, full=True)

    def _ask_quit(self):
        if tk.messagebox.askokcancel("Quit", "You want to quit now?"):
            self._master.destroy()

    def _ask_name(self):
        name = tk.simpledialog.askstring("Good Job!",
                                         "Please enter your name:")
        return name