Esempio n. 1
0
    def __init__(self, level=FileName.Level_0):
        self._level = level
        FileManager.instance().load_level(self._level)

        # устаанвлиавет флаги на дубпг
        self._debug = FileManager.instance().get(FileName.Setting, 'debug')
        self._walls_debug = FileManager.instance().get(FileName.Setting,
                                                       'wall_debug')

        # первая тоска стены
        self._start = None

        self._world = World(debug=True)
        self._factory = DebugFactory(self._world, self._level,
                                     self._walls_debug)
        self._factory.create()
        GameManager.instance().create(self._world,
                                      self._factory.create_player())

        self._screen_w = pygame.display.Info().current_w
        self._screen_h = pygame.display.Info().current_h

        self._zoom = 0

        self._render = WorldRender()
        self._camera = Camera(self._screen_w, self._screen_h)
        self._render.set_camera(self._camera)

        self._direction = 0

        self._class = StupidEnemy
        self._target = None
Esempio n. 2
0
 def draw_world(self, w: World):
     """
     :param w: экзепляр класса World
     """
     actors = w.get_all_actors()
     for actor in actors:
         if isinstance(actor, Actor):
             if actor.visible:
                 name = actor.structure
                 shape = actor.shape
                 if not self._camera.intersection(shape.bb):
                     continue
                 body = actor.body
                 color = actor.color
                 if isinstance(color, str):
                     color = pygame.color.THECOLORS[actor.color]
                 if name == Structure.Circle:
                     pygame.draw.circle(
                         self._screen, color,
                         self._transform_coord(body, 0, 0),
                         int(self._transform_segment(shape.radius)))
                 if name == Structure.Polygon:
                     pygame.draw.polygon(
                         self._screen, color,
                         self._transform_coord(body, shape.get_vertices()))
Esempio n. 3
0
    def start(self):
        #  ======================================================
        #  start message

        def clear():
            return os.system('cls')

        clear()

        def wel():
            welmsg = [
                58 * '─', '|  0.01  |' + 12 * ' ' +
                'pyCestra -  World Server' + 11 * ' ' + '|', 58 * "─"
            ]
            for x in welmsg:
                print(bcolors.blue + x + bcolors.cend)

        wel()

        #  ======================================================
        #  connection test

        self.log.info('Connection Test...')
        database = dataSource.Database()
        if database.get_connection():
            self.log.info('Connection Successfully')
        else:
            self.log.warning('Connection ERROR')
            sys.exit(0)

        #  ======================================================
        #  world class test
        world = World()
        world.createWorld()
        #  ======================================================
        #  exchange client test

        exchangeTransferList = []
        exClient = ExchangeClient()
        exClient.initialize(self.config.get_exchange_ip(),
                            self.config.get_exchange_port(),
                            exchangeTransferList)

        self.log.debug('Game Server Start')
        GameServer().initialize(self.config.get_world_ip(),
                                self.config.get_world_port(),
                                exchangeTransferList, world)
Esempio n. 4
0
    def __init__(self):
        AudioManager.instance().set_music(
            'resources/sounds/peacefullmusic.mp3')
        self._level = FileName.Level_0
        self._world = World()
        self._factory = DemoFactory(self._world, self._level)
        self._player = self._factory.create_player()
        GameManager.instance().create(self._world, self._player)
        self._factory.create()

        self._screen_h = pygame.display.Info().current_h
        self._render = WorldRender()
        self._camera = Camera(Core.instance().info().current_w, self._screen_h)
        self._render.set_camera(self._camera)
        self._direction = 0
        self._zoom = 0
        # test PlayerUI
        self._playerUI = PlayerUI(self._player)
Esempio n. 5
0
 def loadWorld(fileName):
     from game.world.world import World
     if os.path.exists(fileName):
         with open(fileName, "r") as file:
             content = file.read()
             content = json.loads(content)
             world = World.fromJson(content)
             return world
     return None
Esempio n. 6
0
class DebugMode(Mode):
    def __init__(self, level=FileName.Level_0):
        self._level = level
        FileManager.instance().load_level(self._level)

        # устаанвлиавет флаги на дубпг
        self._debug = FileManager.instance().get(FileName.Setting, 'debug')
        self._walls_debug = FileManager.instance().get(FileName.Setting,
                                                       'wall_debug')

        # первая тоска стены
        self._start = None

        self._world = World(debug=True)
        self._factory = DebugFactory(self._world, self._level,
                                     self._walls_debug)
        self._factory.create()
        GameManager.instance().create(self._world,
                                      self._factory.create_player())

        self._screen_w = pygame.display.Info().current_w
        self._screen_h = pygame.display.Info().current_h

        self._zoom = 0

        self._render = WorldRender()
        self._camera = Camera(self._screen_w, self._screen_h)
        self._render.set_camera(self._camera)

        self._direction = 0

        self._class = StupidEnemy
        self._target = None

    def show(self):
        pass

    def update(self, delta: float):

        if self._zoom > 0 and self._camera.zoom < self._camera.max_zoom:
            self._camera.zoom += 0.01
        elif self._zoom < 0 and self._camera.zoom > self._camera.min_zoom:
            self._camera.zoom -= 0.01

        self._world.step(delta)
        if self._direction & 1 != 0:
            self._camera.pos[1] += 10
        if self._direction & 2 != 0:
            self._camera.pos[0] += 10
        if self._direction & 4 != 0:
            self._camera.pos[1] -= 10
        if self._direction & 8 != 0:
            self._camera.pos[0] -= 10

    def render(self):
        self._render.draw_world(self._world)

    def destroy(self):
        pass

    def get_point(self):
        x = pygame.mouse.get_pos()[0] - self._screen_w / 2
        y = self._screen_h - pygame.mouse.get_pos()[1] - self._screen_h / 2
        x /= self._camera.zoom
        y /= self._camera.zoom
        x = self._camera.pos[0] + x
        y = self._camera.pos[1] + y
        return x, y

    def _key(self, event):
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE or event.type == pygame.QUIT:
                FileManager.instance().save_level()
                UIManager.instance().set_screen(MenuMode())
            else:
                if event.key == pygame.K_f:
                    self.save()
                if event.key == pygame.K_1:
                    self._class = StupidEnemy
                elif event.key == pygame.K_3:
                    self._class = SetterItem
                elif event.key == pygame.K_4:
                    self._class = Barrel
                elif event.key == pygame.K_5:
                    self._class = Box
                elif event.key == pygame.K_2:
                    self._class = Player
                elif event.key == pygame.K_w:
                    self._direction |= 1
                elif event.key == pygame.K_d:
                    self._direction |= 2
                elif event.key == pygame.K_s:
                    self._direction |= 4
                elif event.key == pygame.K_a:
                    self._direction |= 8
                elif event.key == pygame.K_q and self._target is not None:
                    self._target.type_item += 1
                elif event.key == pygame.K_MINUS:
                    self._zoom = -1
                elif event.key == pygame.K_EQUALS:
                    self._zoom = 1
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_w:
                self._direction &= ~1
            if event.key == pygame.K_d:
                self._direction &= ~2
            if event.key == pygame.K_s:
                self._direction &= ~4
            if event.key == pygame.K_a:
                self._direction &= ~8
            if event.key == pygame.K_MINUS or event.key == pygame.K_EQUALS:
                self._zoom = 0

    def _mouse(self, event):

        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 3 and self._walls_debug:
                s = self._world.get_space().point_query(
                    self.get_point(), 0,
                    pymunk.ShapeFilter(mask=pymunk.ShapeFilter.ALL_MASKS))
                if len(s) > 0:
                    s = s[-1][0].body.data
                    if isinstance(s, Wall):
                        self._world.remove_actor(s)
            if event.button == 1:
                if self._walls_debug:
                    s = self._world.get_space().point_query(
                        self.get_point(), 0,
                        pymunk.ShapeFilter(mask=pymunk.ShapeFilter.ALL_MASKS))
                    if len(s) == 0:
                        return
                    if self._start is None:
                        self._start = s[0][0]
                    else:
                        end = s[0][0]
                        x1 = min(self._start.body.position[0],
                                 end.body.position[0])
                        y1 = min(self._start.body.position[1],
                                 end.body.position[1])
                        x2 = max(self._start.body.position[0],
                                 end.body.position[0])
                        y2 = max(self._start.body.position[1],
                                 end.body.position[1])
                        v = ((x1 - 5, y1 - 5), (x1 - 5, y2 + 5),
                             (x2 + 5, y2 + 5), (x2 + 5, y1 - 5))
                        GameManager.instance().add_actor(
                            Wall((x1 + x2) / 2, (y1 + y2) / 2,
                                 Structure.Polygon, Actor.center(v)))
                        self._start = None
                else:
                    x, y = self.get_point()
                    s = self._world.get_space().point_query(
                        self.get_point(), 10,
                        pymunk.ShapeFilter(mask=pymunk.ShapeFilter.ALL_MASKS))
                    if len(s) > 0:
                        s = s[0][0].body.data
                        if not isinstance(s, Wall):
                            self._world.remove_actor(s)
                    else:
                        self._target = None
                        if self._class == SetterItem:
                            self._target = self._class(x, y)
                            GameManager.instance().add_actor(self._target)
                        else:
                            GameManager.instance().add_actor(self._class(x, y))

    def call(self, event):
        self._key(event)
        self._mouse(event)

    @staticmethod
    def _list_vertex(vertex):
        ll = []
        for i in vertex:
            ll.append((i[0], i[1]))
        return tuple(ll)

    def save(self):
        actors = self._world.get_all_actors()
        if self._walls_debug:
            FileManager.instance().set(self._level, 'Walls', [])
        else:
            FileManager.instance().set(self._level, 'Enemy', [])
            FileManager.instance().set(self._level, 'Box', [])
            FileManager.instance().set(self._level, 'Heal', [])
            FileManager.instance().set(self._level, 'Barrel', [])
            FileManager.instance().set(self._level, 'Player', [])
            FileManager.instance().set(self._level, 'Items', [])

        for actor in actors:
            if self._walls_debug:
                if isinstance(actor, Wall):
                    inf = (actor.pos[0], actor.pos[1],
                           self._list_vertex(actor.shape.get_vertices()))
                    FileManager.instance().get(self._level,
                                               'Walls').append(inf)
            else:
                if isinstance(actor, StupidEnemy):
                    inf = (actor.pos[0], actor.pos[1])
                    FileManager.instance().get(self._level,
                                               'Enemy').append(inf)
                if isinstance(actor, Box):
                    inf = (actor.pos[0], actor.pos[1])
                    FileManager.instance().get(self._level, 'Box').append(inf)
                if isinstance(actor, Barrel):
                    inf = (actor.pos[0], actor.pos[1])
                    FileManager.instance().get(self._level,
                                               'Barrel').append(inf)
                if isinstance(actor, SetterItem):
                    inf = (actor.pos[0], actor.pos[1], actor.type_item)
                    FileManager.instance().get(self._level,
                                               'Items').append(inf)
                if isinstance(actor, Player):
                    inf = (actor.pos[0], actor.pos[1])
                    FileManager.instance().get(self._level,
                                               'Player').append(inf)
                if isinstance(actor, Heal):
                    inf = (actor.pos[0], actor.pos[1], 4)
                    FileManager.instance().get(self._level,
                                               'Items').append(inf)
                if isinstance(actor, Boost):
                    inf = (actor.pos[0], actor.pos[1], 5)
                    FileManager.instance().get(self._level,
                                               'Items').append(inf)
                if isinstance(actor, TripleGunItem):
                    inf = (actor.pos[0], actor.pos[1], 2)
                    FileManager.instance().get(self._level,
                                               'Items').append(inf)
                if isinstance(actor, ExpBulletItem):
                    inf = (actor.pos[0], actor.pos[1], 3)
                    FileManager.instance().get(self._level,
                                               'Items').append(inf)
                if isinstance(actor, Portal):
                    inf = (actor.pos[0], actor.pos[1], 1)
                    FileManager.instance().get(self._level,
                                               'Items').append(inf)
Esempio n. 7
0
class GameMode(Mode):
    def __init__(self, level=FileName.Level_0):
        AudioManager.instance().set_music(
            'resources/sounds/peacefullmusic.mp3')
        self._level = level
        FileManager.instance().load_level(self._level)
        self._world = World()
        self._factory = Factory(self._world, self._level)
        self._player = self.create_player()

        GameManager.instance().create(self._world, self._player)
        self._factory.create()

        self._screen_h = pygame.display.Info().current_h
        self._render = WorldRender()
        self._camera = Camera(Core.instance().info().current_w, self._screen_h)
        self._render.set_camera(self._camera)
        self._direction = 0
        self._zoom = 0
        # test PlayerUI
        self._playerUI = PlayerUI(self._player)

    def create_player(self):
        stat = {
            "Heal": FileManager.instance().get(FileName.Player_Stats, "Heal"),
            "Gun": FileManager.instance().get(FileName.Player_Stats, "Gun"),
            "Bullet": FileManager.instance().get(FileName.Player_Stats,
                                                 "Bullet"),
            "MaxHeal": FileManager.instance().get(FileName.Player_Stats,
                                                  "MaxHeal")
        }
        if self._level == FileName.Level_0:
            stat = Player.get_default_stats()
        if FileManager.instance().get(FileName.Setting, "god_mode"):
            stat = {"Heal": 10**6, "Gun": 1, "Bullet": 1, "MaxHeal": 10**6}
        return self._factory.create_player(stat)

    def show(self):
        pass

    def update(self, delta: float):
        if self._zoom > 0 and self._camera.zoom < self._camera.max_zoom:
            self._camera.zoom += 0.01
        elif self._zoom < 0 and self._camera.zoom > self._camera.min_zoom:
            self._camera.zoom -= 0.01

        self._player.move(self._direction)
        self._world.step(delta)
        self._camera.pos = self._player.body.position

        if self._player.life <= 0:
            AudioManager.instance().play_sound(SoundName.Sound7)
            UIManager.instance().set_screen(MenuMode())
        self._playerUI.update()

    def render(self):
        self._render.draw_world(self._world)
        self._playerUI.draw()

    def destroy(self):
        FileManager.instance().set(FileName.Player_Stats, "Heal",
                                   self._player.life)
        FileManager.instance().set(FileName.Player_Stats, "Gun",
                                   self._player.type_gun)
        FileManager.instance().set(FileName.Player_Stats, "Bullet",
                                   self._player.type_bul)
        FileManager.instance().set(FileName.Player_Stats, "MaxHeal",
                                   self._player.maxLife)
        FileManager.instance().save_player_stats()

    def call(self, event):
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE or event.type == pygame.QUIT:
                UIManager.instance().set_screen(MenuMode())
            else:
                if event.key == pygame.K_r:
                    self.reset()
                if event.key == pygame.K_w:
                    self._direction |= 1
                if event.key == pygame.K_d:
                    self._direction |= 2
                if event.key == pygame.K_s:
                    self._direction |= 4
                if event.key == pygame.K_a:
                    self._direction |= 8
                if event.key == pygame.K_MINUS:
                    self._zoom = -1
                if event.key == pygame.K_EQUALS:
                    self._zoom = 1
        if event.type == pygame.MOUSEMOTION:
            mouse_pos = pygame.mouse.get_pos()
            p = self._camera.transform_coord(self._player.pos.x,
                                             self._player.pos.y)
            self._player.set_direction(-math.atan2(mouse_pos[1] -
                                                   p[1], mouse_pos[0] - p[0]))

        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                self._player.shot(True)
        if event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                self._player.shot(False)

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_w:
                self._direction &= ~1
            if event.key == pygame.K_d:
                self._direction &= ~2
            if event.key == pygame.K_s:
                self._direction &= ~4
            if event.key == pygame.K_a:
                self._direction &= ~8
            if event.key == pygame.K_MINUS or event.key == pygame.K_EQUALS:
                self._zoom = 0

    def reset(self):
        self.destroy()
        UIManager.instance().set_screen(GameMode())
Esempio n. 8
0
class GameMode(Mode):
    def __init__(self):
        AudioManager.instance().set_music(
            'resources/sounds/peacefullmusic.mp3')
        self._level = FileName.Level_0
        self._world = World()
        self._factory = DemoFactory(self._world, self._level)
        self._player = self._factory.create_player()
        GameManager.instance().create(self._world, self._player)
        self._factory.create()

        self._screen_h = pygame.display.Info().current_h
        self._render = WorldRender()
        self._camera = Camera(Core.instance().info().current_w, self._screen_h)
        self._render.set_camera(self._camera)
        self._direction = 0
        self._zoom = 0
        # test PlayerUI
        self._playerUI = PlayerUI(self._player)

    def show(self):
        pass

    def update(self, delta: float):
        if self._zoom > 0 and self._camera.zoom < self._camera.max_zoom:
            self._camera.zoom += 0.01
        elif self._zoom < 0 and self._camera.zoom > self._camera.min_zoom:
            self._camera.zoom -= 0.01

        self._player.move(self._direction)
        self._world.step(delta)
        self._camera.pos = self._player.body.position

        if self._player.life <= 0:
            UIManager.instance().set_screen(MenuMode())
        self._playerUI.update()

    def render(self):
        self._render.draw_world(self._world)
        self._playerUI.draw()

    def destroy(self):
        pass

    def call(self, event):
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE or event.type == pygame.QUIT:
                UIManager.instance().set_screen(MenuMode())
            else:
                if event.key == pygame.K_r:
                    self.reset()
                if event.key == pygame.K_w:
                    self._direction |= 1
                if event.key == pygame.K_d:
                    self._direction |= 2
                if event.key == pygame.K_s:
                    self._direction |= 4
                if event.key == pygame.K_a:
                    self._direction |= 8
                if event.key == pygame.K_MINUS:
                    self._zoom = -1
                if event.key == pygame.K_EQUALS:
                    self._zoom = 1
        if event.type == pygame.MOUSEMOTION:
            mouse_pos = pygame.mouse.get_pos()
            p = self._camera.transform_coord(self._player.pos.x,
                                             self._player.pos.y)
            self._player.set_direction(-math.atan2(mouse_pos[1] -
                                                   p[1], mouse_pos[0] - p[0]))

        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                self._player.shot(True)
        if event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                self._player.shot(False)

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_w:
                self._direction &= ~1
            if event.key == pygame.K_d:
                self._direction &= ~2
            if event.key == pygame.K_s:
                self._direction &= ~4
            if event.key == pygame.K_a:
                self._direction &= ~8
            if event.key == pygame.K_MINUS or event.key == pygame.K_EQUALS:
                self._zoom = 0

    def reset(self):
        self.destroy()
        UIManager.instance().set_screen(GameMode())
Esempio n. 9
0
 def initWorld(self):
     self.world = World()
     self.world.generateWorld()
Esempio n. 10
0
class GameScene(Scene):
    def __init__(self, app):
        super().__init__(app)
        self.initNew()
        self.initGame()
        self.initComponents()
        self.initOverlay()

        self.isPaused = False

    def initNew(self):
        self.initWorld()
        self.initPlayer()

    def initWorld(self):
        self.world = World()
        self.world.generateWorld()

    def initGame(self):
        self.player = None
        self.previewWidth = 10
        Constants.blockSize = self.app.width / (self.previewWidth * 2 + 1)
        self.previewHeight = math.ceil(self.app.height / Constants.blockSize)

        self.offset = 5  # load outside canvas to hide buffering
        self.renderOffset = 7.6
        self.holdPos = False
        self.holdPosTickThresh = 30
        self.holdPosTick = 0

        self.difficulty = 2
        self.difficultyLabels = ["Peaceful", "Easy", "Normal", "Hard"]

    def initPlayer(self):
        self.player = Player(self.world)
        sword = ItemStack(Material.SWORD, 1)
        fire_sword = ItemStack(Material.FIRE_SWORD, 1)
        ice_sword = ItemStack(Material.ICE_SWORD, 1)

        pickaxe = ItemStack(Material.PICKAXE, 1)
        explosive_pickaxe = ItemStack(Material.EXPLOSIVE_PICKAXE, 1)

        inventory = self.player.getInventory()

        inventory.addItem(sword)
        inventory.addItem(fire_sword)
        inventory.addItem(ice_sword)

        inventory.addItem(pickaxe)
        inventory.addItem(explosive_pickaxe)

        y = self.world.getHighestBlock(0)
        self.player.position = Vector2D(0, y)
        self.world.addEntity(self.player)

    def initComponents(self):
        textFont = Fonts.getFont(Fonts.Roboto_Mono, 30)
        self.label = Label(self.window,
                           self.app.width / 2,
                           5 * self.app.height / 6,
                           text="",
                           font=textFont)
        self.label.x = self.app.width / 2
        self.label.y = 6 * self.app.height / 7

        self.addComponent(self.label)

    def initOverlay(self):
        textFont = Fonts.getFont(Fonts.Courier, 30)
        window = self.window
        width, height = self.app.width, self.app.height

        unit = height / 4

        self.volumeButton = ImageButton(window,
                                        2 * width / 3,
                                        1.6 * unit,
                                        Assets.assets["volume_on"],
                                        fillColor=Colors.WHITE)
        self.volumeButton.setOnClickListener(self.toggleVolume)

        self.respawnButton = Button(window,
                                    width / 2,
                                    2 * unit,
                                    font=textFont,
                                    text="Respawn",
                                    fillColor=Colors.WHITE,
                                    padding=10)
        self.respawnButton.setOnClickListener(self.respawnPlayer)

        self.resumeButton = Button(window,
                                   width / 2,
                                   2 * unit,
                                   font=textFont,
                                   text="Resume Game",
                                   fillColor=Colors.WHITE,
                                   padding=10)
        self.resumeButton.setOnClickListener(self.togglePause)

        self.difficultyButton = Button(window,
                                       width / 2,
                                       2.5 * unit,
                                       font=textFont,
                                       text="Difficulty: Normal",
                                       fillColor=Colors.WHITE,
                                       padding=10)
        self.difficultyButton.setOnClickListener(self.cycleDifficulty)

        self.quitButton = Button(window,
                                 width / 2,
                                 3 * unit,
                                 font=textFont,
                                 text="Main Menu",
                                 fillColor=Colors.WHITE,
                                 padding=10)
        self.quitButton.setOnClickListener(self.quit)

        self.overlayComponents = [
            self.resumeButton, self.quitButton, self.respawnButton,
            self.volumeButton, self.difficultyButton
        ]

        for component in self.overlayComponents:
            component.setEnabled(False)

        self.addComponents(self.overlayComponents)

    def drawComponents(self):
        self.drawBackground()
        self.drawTerrain()
        self.drawEntities()
        self.drawInventory()
        self.drawOverlay()

        super().drawComponents()

    def drawBackground(self):
        window = self.window
        player = self.player
        bg = Assets.assets["game_background"]
        bgSize = bg.get_size()
        windowSize = window.get_size()
        x = windowSize[0] - bgSize[0] / 2 - player.position[0]
        y = windowSize[1] - bgSize[1] / 2 + player.position[1]
        window.blit(bg, (x, y))

    def drawTerrain(self):
        world = self.world
        height, width = self.previewHeight, self.previewWidth
        player = self.player
        px, py = player.position
        offset = self.offset
        renderOffset = self.renderOffset

        size = Constants.blockSize
        for y in range(-height - offset, height + offset):
            for x in range(-width - offset, width + offset):
                bx = px + x
                by = py - y
                block = world.getBlock((bx, by))

                renderX = (x + width) * size
                renderY = (y + height - renderOffset) * size
                self.drawBlock(block, (renderX, renderY))

    def drawBlock(self, block, position):
        window = self.window
        texture = Assets.assets["textures"][block.getType().getId()][0]

        x, y = position
        size = Constants.blockSize
        blockRect = pygame.Rect(0, 0, size, size)
        blockRect.center = (x + size / 2, y + size / 2)

        window.blit(texture, blockRect)

    def drawEntities(self):
        window = self.window
        world = self.world
        px, py = self.player.position
        cx, cy = self.app.width / 2, self.app.height / 2

        for entity in world.entities:
            x, y = entity.position
            rx = cx + (x - px) * Constants.blockSize
            ry = cy + (-y + py) * Constants.blockSize
            if not isinstance(entity, Player):
                ry += self.renderOffset
            entity.draw(window, rx, ry)

    def drawInventory(self):
        window = self.window
        inventory = self.player.getInventory()
        width = inventory.getDimensions()[0]

        cellSize = 40
        offset = 10
        for i in range(width):
            rect = pygame.Rect(i * cellSize + offset, offset, cellSize,
                               cellSize)
            borderWidth = 1
            if i == self.player.equipIndex:
                borderWidth = 3
            pygame.draw.rect(self.window, Colors.BLACK, rect, borderWidth)

            item = inventory[0][i]
            if item.getType() != Material.AIR:
                id = item.getType().getId()
                texture = Assets.assets["textures"][id]
                if item.getType() in Tools.tools:
                    texture = texture[0]
                else:
                    texture = texture[1]
                tWidth, tHeight = texture.get_size()
                tRect = pygame.Rect(0, 0, tWidth, tHeight)
                tRect.center = rect.center
                window.blit(texture, tRect)

                amount = item.getAmount()
                self.drawItemCount(amount, tRect)

    def drawItemCount(self, amount, rect):
        font = Fonts.getFont(Fonts.Courier, 20)
        if amount > 1:
            window = self.window
            x, y = rect.bottomright
            countLabel = Label(window,
                               x,
                               y,
                               str(amount),
                               font,
                               color=Colors.BLACK)
            countLabel.draw()

    def drawOverlay(self):
        if Constants.FILTER is None:
            Constants.FILTER = pygame.Surface(
                (self.app.width, self.app.height))
            Constants.FILTER.set_alpha(100)
            Constants.FILTER.fill((255, 255, 255))

        for component in self.overlayComponents:
            enabled = not self.player.isAlive or self.isPaused
            component.setEnabled(enabled)

        if not self.player.isAlive:
            self.window.blit(Constants.FILTER, (0, 0))
            self.resumeButton.setEnabled(False)
        elif self.isPaused:
            self.window.blit(Constants.FILTER, (0, 0))
            self.respawnButton.setEnabled(False)

    def onKeyPress(self, keys, mods):
        super().onKeyPress(keys, mods)
        if self.isPaused: return
        player = self.player

        if keys[pygame.K_a]:
            player.move(-1, 0, walk=True)
        elif keys[pygame.K_d]:
            player.move(1, 0, walk=True)
        else:
            if not self.holdPos:
                player.faceDirection(0, False)
        player.update()

    def onKeyDown(self, key):
        if key == pygame.K_ESCAPE and self.player.isAlive:
            self.togglePause()

        if self.isPaused: return
        player = self.player
        if key == pygame.K_c:
            self.world.rngSpawnEntity(self.player, spawn=True)
        else:
            for i in range(9):
                if key == pygame.K_1 + i:
                    player.equipIndex = i
                    break
        if not player.isJumping and key == pygame.K_SPACE:
            player.jump()
        player.update()

    def onMouseClick(self, mousePos):
        super().onMouseClick(mousePos)
        if self.isPaused: return
        item = self.player.getEquippedItem()

        if item.getType() in Tools.tools:
            self.breakBlock(mousePos)
        elif item.getType() in Weapons.weapons:
            self.attack(mousePos, item)

    def onMouseRightClick(self, mousePos):
        if self.isPaused: return
        player = self.player
        type = player.getEquippedItem().getType()
        if (type not in Tools.tools and type not in Weapons.weapons
                and type != Material.AIR):
            _, bx, by = self.getBlockFromMousePos(mousePos)
            world = self.world
            item = player.getEquippedItem()
            world.setBlock(item.getType(), (bx, by))
            item.setAmount(item.getAmount() - 1)
            if item.getAmount() == 0:
                item.material = Material.AIR
                item.amount = 1

    def onMouseScroll(self, scroll):
        player = self.player
        inventory = player.getInventory()
        player.equipIndex = (player.equipIndex - scroll) % (inventory.width)

        self.label.setText(player.getEquippedItem().getType().getName())

    def onTick(self):
        if self.isPaused: return
        self.world.tick()
        if self.holdPos:
            self.holdPosTick += 1
            if self.holdPosTick == self.holdPosTickThresh:
                self.holdPosTick = 0
                self.holdPos = False
        if not self.player.isAlive:
            self.isPaused = True

    def onLoad(self):
        if self.isPaused:
            self.togglePause()
        self.onMouseScroll(0)
        self.difficulty = self.world.difficulty

    def getBlockFromMousePos(self, mousePos):
        rx, ry = mousePos
        px, py = self.player.position
        size = Constants.blockSize
        height, width = self.previewHeight, self.previewWidth
        offset = 0.5

        x = rx / size - width - offset
        y = ry / size - height + self.renderOffset - offset

        bx = px + x
        by = py - y
        block = self.world.getBlock((bx, by))
        return block, bx, by

    def breakBlock(self, mousePos):
        block, bx, by = self.getBlockFromMousePos(mousePos)
        if block.getType() != Material.AIR:
            item = self.player.getEquippedItem()
            radius = Tools.tools[item.getType()]
            self.destroyBlock((bx, by))
            for dx in range(radius):
                for dy in range(radius):
                    self.destroyBlock((bx + dx, by + dy))

    def destroyBlock(self, coordinate):
        block = self.world.getBlock(coordinate)
        if block.getType() != Material.AIR:
            self.player.getInventory().addItem(ItemStack(block.getType(), 1))
            self.world.setBlock(Material.AIR, coordinate)

    def attack(self, mousePos, item):
        mx = mousePos[0]
        cx = self.app.width / 2
        player = self.player
        direction = 1 if mx > cx else -1

        dead = []
        self.player.faceDirection(direction, False)
        self.holdPos = True

        weapon = Weapons.weapons[item.getType()]
        damage = weapon.damage
        reach = weapon.reach

        targets = []

        for entity in self.world.entities:
            if isinstance(entity, Enemy):
                pPos = player.position
                ePos = entity.position

                px, ex = pPos[0], ePos[0]

                relativeDelta = 1 if ex > px else -1
                distance = pPos.distance(ePos)
                if relativeDelta == direction and distance <= reach:
                    knockback = True
                    status = 0
                    if weapon.getType() == Material.ICE_SWORD:
                        knockback = False
                        status = 1
                    elif weapon.getType() == Material.FIRE_SWORD:
                        status = 2
                    targets.append(
                        (entity, damage, relativeDelta, knockback, status))

        for entity, damage, relativeDelta, knockback, status in targets:
            entity.damage(damage, relativeDelta, knockback)
            if status == 1:
                entity.freeze()
            elif status == 2:
                entity.burn()

    def togglePause(self):
        self.isPaused = not self.isPaused
        self.difficulty = self.world.difficulty

    def toggleVolume(self):
        self.app.volumeOn = not self.app.volumeOn
        if self.app.volumeOn:
            self.volumeButton.setImage(Assets.assets["volume_on"])
            self.app.musicChannel.set_volume(1)
        else:
            self.volumeButton.setImage(Assets.assets["volume_off"])
            self.app.musicChannel.set_volume(0)

    def cycleDifficulty(self):
        self.difficulty = (self.difficulty + 1) % 4
        self.world.setDifficulty(self.difficulty)
        self.difficultyButton.setText(
            f"Difficulty: {self.difficultyLabels[self.difficulty]}")

    def respawnPlayer(self):
        self.player.respawn()
        self.isPaused = False

    def quit(self):
        Utility.save(self.world)
        pygame.event.wait()
        self.app.changeScene("main")