コード例 #1
0
ファイル: main.py プロジェクト: erdOne/TowerDefence
class MyApp:
    """The main class."""
    def __init__(self):
        """Start the app."""
        self.base = ShowBase()
        self.base.disableMouse()

        filters = CommonFilters(self.base.win, self.base.cam)
        filters.setBloom(blend=(0, 0, 0, 1))
        self.base.render.setShaderAuto(
            BitMask32.allOn() & ~BitMask32.bit(Shader.BitAutoShaderGlow))
        ts = TextureStage('ts')
        ts.setMode(TextureStage.MGlow)
        tex = self.base.loader.loadTexture('models/black.png')
        self.base.render.setTexture(ts, tex)

        self.terrain = Terrain(self.base.render, self.base.loader)

        minimap_size = 200
        self.minimap = Minimap(
            self.base.win.makeDisplayRegion(
                1 - minimap_size / self.base.win.getProperties().getXSize(), 1,
                1 - minimap_size / self.base.win.getProperties().getYSize(),
                1))
        self.minimap.node_path.reparentTo(self.base.render)

        # self.light_nodes =
        self.set_lights()
        self.set_fog()

        self.key_state = dict.fromkeys(Object.values(config.key_map.character),
                                       False)
        self.set_key_state_handler()

        self.game_state = "pause"
        self.toggle_pause()

        self.selection = Selection(self.base.loader, self.terrain.geom_node)
        self.selection_text = OnscreenText(
            mayChange=True,
            scale=0.07,
            align=TextNode.ALeft,
            pos=(0.02 - self.base.getAspectRatio(), 1 - 0.07))
        self.timer_text = OnscreenText(mayChange=True,
                                       scale=0.07,
                                       align=TextNode.ALeft,
                                       pos=(0.02 - self.base.getAspectRatio(),
                                            -1 + 0.02 + 0.07))

        self.enemies = set()

        self.base.accept(config.key_map.utility.pause, self.toggle_pause)
        self.base.accept(
            "q", lambda: self.selection.advance_tower(self.base.loader))
        self.base.accept("mouse1", self.player_click)
        self.base.cam.node().setCameraMask(BitMask32.bit(0))
        self.base.setBackgroundColor(*config.map_params.colors.sky)

        self.player = Player()
        self.base.taskMgr.add(lambda task: self.terrain.start_up(), "start up")
        self.mouse_pos = (0.0, 0.0)
        self.base.taskMgr.add(self.move_player_task, "move_player_task")
        self.base.taskMgr.add(self.move_enemies_task, "move_enemies_task")
        self.base.taskMgr.add(self.player_select_task, "player_select_task")
        self.base.taskMgr.add(self.tower_task, "tower_task")
        self.base.taskMgr.add(self.check_end_game, "check_end_game_task")
        rand = Random()
        rand.seed(config.map_params.seed)
        self.base.taskMgr.doMethodLater(1,
                                        self.clock_task,
                                        'clock_task',
                                        extraArgs=[rand])
        self.rounds = 0
        self.coin = 40
        # self.base.setFrameRateMeter(True)

    def set_fog(self):
        """Set render distance of camera."""
        fog = Fog("Scene fog")
        fog.setColor(0.7, 0.7, 0.7)
        fog.setExpDensity(0.01)
        # self.terrain.geom_node.setFog(fog)
        self.base.camLens.setFar(4000.0)
        return fog

    def set_lights(self):
        """Set up the lights."""
        light_nodes = [None] * 9
        for i, dirs in zip(range(9), [0] + Object.values(directions) * 2):
            dlight = DirectionalLight(f"directional light {i}")
            if i <= 4:
                dlight.setColor((0.5, 0.5, 0.5, 0.8))
            else:
                dlight.setColor((2, 2, 2, 2))
            light_nodes[i] = self.base.render.attachNewNode(dlight)
            if i == 0:
                light_nodes[i].setPos(0, 0, 1)
            else:
                light_nodes[i].setPos(*dirs, 0)
            light_nodes[i].lookAt(0, 0, 0)
            if i <= 4:
                self.base.render.setLight(light_nodes[i])
                self.terrain.terrain_node.clearLight(light_nodes[i])
            else:
                # self.terrain.terrain_node.setLight(light_nodes[i])
                pass

        alight = AmbientLight('ambient light')
        alight.setColor((0.3, 0.3, 0.3, 1))
        ambient_light_node = self.base.render.attachNewNode(alight)
        self.base.render.setLight(ambient_light_node)
        return light_nodes, ambient_light_node

    def set_key_state_handler(self):
        """Accept key and records to key_state."""
        def set_key_state(key, state):
            self.key_state[key] = state

        for key in self.key_state:
            self.base.accept(key, set_key_state, [key, True])
            self.base.accept(key + "-up", set_key_state, [key, False])

    def toggle_pause(self):
        """Toggle pause."""
        if self.game_state == "ended":
            return
        if self.game_state == "pause":
            self.game_state = "active"
            props = WindowProperties()
            props.setCursorHidden(True)
            props.setMouseMode(WindowProperties.M_confined)
            self.base.win.requestProperties(props)
        else:
            self.game_state = "pause"
            props = WindowProperties()
            props.setCursorHidden(False)
            props.setMouseMode(WindowProperties.M_absolute)
            self.base.win.requestProperties(props)
        print("toggled pause")

    def move_player_task(self, task):  # pylint: disable=unused-argument
        """The task that handles player movement."""
        if self.game_state != "active":
            return Task.cont
        if not self.base.mouseWatcherNode.hasMouse():
            self.toggle_pause()
            return Task.cont
        self.player.update_pos(self.key_state,
                               ClockObject.getGlobalClock().getDt(),
                               lambda pos: self.terrain.get_tile(pos).walkable)
        self.mouse_pos = (
            self.mouse_pos[0] + self.base.mouseWatcherNode.getMouseX(),
            self.mouse_pos[1] + self.base.mouseWatcherNode.getMouseY(),
        )
        self.player.update_hpr(self.mouse_pos)
        self.base.win.movePointer(0,
                                  self.base.win.getXSize() // 2,
                                  self.base.win.getYSize() // 2)
        self.base.camera.setPos(self.player.pos)
        self.minimap.set_pos(self.player.pos, self.player.hpr)
        self.base.camera.setHpr(self.player.hpr)
        self.terrain.update_player_pos(tuple(self.player.pos))
        return Task.cont

    def move_enemies_task(self, task):  # pylint: disable=unused-argument
        """The task that handles enemy movement."""
        if self.game_state != "active":
            return Task.cont
        for enemy in set(self.enemies):
            if not enemy.generated:
                enemy.generate(self.terrain.path_finder, self.base.loader,
                               self.terrain.geom_node, self.terrain.get_tile)
            if not enemy.check_active():
                self.enemies.remove(enemy)
                continue
            # enemy.model.setPos(self.player.pos)
            enemy.move(ClockObject.getGlobalClock().getDt())
        return Task.cont

    def tower_task(self, task):
        if self.game_state != "active":
            return Task.cont
        for tower in set(self.terrain.towers):
            if not tower.generated:
                tower.generate(self.base.loader, self.terrain.geom_node,
                               self.terrain.get_tile)
            if not tower.check_active():
                self.terrain.towers.remove(tower)
                self.terrain[tower.grid_pos] = Floor()
                continue
            tower.move(ClockObject.getGlobalClock().getDt(), self.enemies)
            tower.generate_bullet(self.base.loader, self.terrain.geom_node,
                                  self.terrain.get_tile)
        return Task.cont

    def player_select_task(self, task):
        self.selection.set_selection(
            self.player.view(self.terrain.get_tile, self.enemies))
        self.selection_text.setText(self.selection.get_text())
        if self.selection.tower_class != Empty:
            self.selection.set_disable(
                self.coin < self.selection.tower_class.cost)
        return Task.cont

    def player_click(self):
        if isinstance(self.selection.select, Floor) and \
                issubclass(self.selection.tower_class, Tower) and \
                self.selection.tower_class.cost <= self.coin:
            self.coin -= self.selection.tower_class.cost
            pos = self.selection.model.getPos()
            coord_pos = (int(pos[0] // config.map_params.unit_size),
                         int(pos[1] // config.map_params.unit_size))
            self.terrain[coord_pos] = self.selection.tower_class(coord_pos)

    def spawn_enemies(self, rand):
        if self.game_state != "active":
            return Task.cont
        dsts = [tower.model.getPos().length() for tower in self.terrain.towers]
        radius = max(dsts + [0]) + 100
        num = (self.rounds + config.game.sep // 2) // config.game.sep
        for _ in range(num):
            theta = rand.uniform(0, 2 * pi)
            enemy = Kikiboss if _ % 5 or num % 5 else Dabi
            self.enemies.add(enemy(Vec3(cos(theta), sin(theta), 0) * radius))
        return Task.again

    def clock_task(self, rand):
        if self.game_state != "active":
            return Task.cont
        self.rounds += 1
        self.coin += 1
        ending = "y" if self.coin <= 1 else "ies"
        self.timer_text.setText(
            f"You have {self.coin} cherr{ending}\n" +
            f"{config.game.sep - self.rounds % config.game.sep}s " +
            f"until wave {self.rounds // config.game.sep + 1}.")
        if self.rounds % config.game.sep == 0:
            return self.spawn_enemies(rand)
        return Task.again

    def check_end_game(self, task):
        if self.terrain[(0, 0)].hp <= 0:
            if self.game_state == "active":
                self.toggle_pause()
            self.game_state = "ended"
            self.terrain.geom_node.setColorScale(0.2, 0.2, 0.2, 1)
            OnscreenText(text="GAME OVER",
                         scale=0.2,
                         fg=(1, 0, 0, 1),
                         align=TextNode.ACenter,
                         pos=(0, 0))
            OnscreenText(text=f"score: {self.rounds}",
                         scale=0.07,
                         align=TextNode.ACenter,
                         pos=(0, -.15))
            return task.done
        return task.cont

    def run(self):
        """Run."""
        self.base.run()