예제 #1
0
    def _get_spawn_point(self, player: Player) -> Optional[ba.Vec3]:
        del player  # Unused.

        # In solo-mode, if there's an existing live player on the map, spawn at
        # whichever spot is farthest from them (keeps the action spread out).
        if self._solo_mode:
            living_player = None
            living_player_pos = None
            for team in self.teams:
                for tplayer in team.players:
                    if tplayer.is_alive():
                        assert tplayer.node
                        ppos = tplayer.node.position
                        living_player = tplayer
                        living_player_pos = ppos
                        break
            if living_player:
                assert living_player_pos is not None
                player_pos = ba.Vec3(living_player_pos)
                points: List[Tuple[float, ba.Vec3]] = []
                for team in self.teams:
                    start_pos = ba.Vec3(self.map.get_start_position(team.id))
                    points.append(
                        ((start_pos - player_pos).length(), start_pos))
                # Hmm.. we need to sorting vectors too?
                points.sort(key=lambda x: x[0])
                return points[-1][1]
        return None
예제 #2
0
    def _update(self) -> None:

        # Update one of our bot lists each time through.
        # First off, remove dead bots from the list. Note that we check
        # exists() here via the bool operator instead of dead; we want to
        # keep them around even if they're just a corpse.
        try:
            bot_list = self._bot_lists[self._bot_update_list] = ([
                b for b in self._bot_lists[self._bot_update_list] if b
            ])
        except Exception:
            bot_list = []
            ba.print_exception('error updating bot list: ' +
                               str(self._bot_lists[self._bot_update_list]))
        self._bot_update_list = (self._bot_update_list +
                                 1) % self._bot_list_count

        # Update our list of player points for the bots to use.
        player_pts = []
        for player in ba.getactivity().players:
            assert isinstance(player, ba.Player)
            try:
                if player.is_alive():
                    assert isinstance(player.actor, Spaz)
                    assert player.actor.node
                    player_pts.append((ba.Vec3(player.actor.node.position),
                                       ba.Vec3(player.actor.node.velocity)))
            except Exception:
                ba.print_exception('error on bot-set _update')

        for bot in bot_list:
            bot.set_player_points(player_pts)
            bot.update_ai()
예제 #3
0
    def _get_target_player_pt(
            self) -> tuple[Optional[ba.Vec3], Optional[ba.Vec3]]:
        """Returns the position and velocity of our target.

        Both values will be None in the case of no target.
        """
        assert self.node
        botpt = ba.Vec3(self.node.position)
        closest_dist: Optional[float] = None
        closest_vel: Optional[ba.Vec3] = None
        closest: Optional[ba.Vec3] = None
        assert self._player_pts is not None
        for plpt, plvel in self._player_pts:
            dist = (plpt - botpt).length()

            # Ignore player-points that are significantly below the bot
            # (keeps bots from following players off cliffs).
            if (closest_dist is None
                    or dist < closest_dist) and (plpt[1] > botpt[1] - 5.0):
                closest_dist = dist
                closest_vel = plvel
                closest = plpt
        if closest_dist is not None:
            assert closest_vel is not None
            assert closest is not None
            return (ba.Vec3(closest[0], closest[1], closest[2]),
                    ba.Vec3(closest_vel[0], closest_vel[1], closest_vel[2]))
        return None, None
예제 #4
0
    def _update(self) -> None:

        # Update one of our bot lists each time through.
        # First off, remove no-longer-existing bots from the list.
        try:
            bot_list = self._bot_lists[self._bot_update_list] = ([
                b for b in self._bot_lists[self._bot_update_list] if b
            ])
        except Exception:
            bot_list = []
            ba.print_exception('Error updating bot list: ' +
                               str(self._bot_lists[self._bot_update_list]))
        self._bot_update_list = (self._bot_update_list +
                                 1) % self._bot_list_count

        # Update our list of player points for the bots to use.
        player_pts = []
        for player in ba.getactivity().players:
            assert isinstance(player, ba.Player)
            try:
                # TODO: could use abstracted player.position here so we
                # don't have to assume their actor type, but we have no
                # abstracted velocity as of yet.
                if player.is_alive():
                    assert isinstance(player.actor, Spaz)
                    assert player.actor.node
                    player_pts.append((ba.Vec3(player.actor.node.position),
                                       ba.Vec3(player.actor.node.velocity)))
            except Exception:
                ba.print_exception('Error on bot-set _update.')

        for bot in bot_list:
            bot.set_player_points(player_pts)
            bot.update_ai()
예제 #5
0
    def _update_player_order(self) -> None:

        # Calc all player distances.
        for player in self.players:
            pos: Optional[ba.Vec3]
            try:
                pos = player.position
            except ba.NotFoundError:
                pos = None
            if pos is not None:
                r_index = player.last_region
                rg1 = self._regions[r_index]
                r1pt = ba.Vec3(rg1.pos[:3])
                rg2 = self._regions[0] if r_index == len(
                    self._regions) - 1 else self._regions[r_index + 1]
                r2pt = ba.Vec3(rg2.pos[:3])
                r2dist = (pos - r2pt).length()
                amt = 1.0 - (r2dist / (r2pt - r1pt).length())
                amt = player.lap + (r_index + amt) * (1.0 / len(self._regions))
                player.distance = amt

        # Sort players by distance and update their ranks.
        p_list = [(player.distance, player) for player in self.players]

        p_list.sort(reverse=True, key=lambda x: x[0])
        for i, plr in enumerate(p_list):
            plr[1].rank = i
            if plr[1].actor:
                node = plr[1].distance_txt
                if node:
                    node.text = str(i + 1) if plr[1].is_alive() else ''
예제 #6
0
    def _get_player_spawn_position(self, player: Player) -> Sequence[float]:

        # Iterate until we find a spawn owned by this team.
        spawn_count = len(self.map.spawn_by_flag_points)

        # Get all spawns owned by this team.
        spawns = [
            i for i in range(spawn_count) if self._flags[i].team is player.team
        ]

        closest_spawn = 0
        closest_distance = 9999.0

        # Now find the spawn that's closest to a spawn not owned by us;
        # we'll use that one.
        for spawn in spawns:
            spt = self.map.spawn_by_flag_points[spawn]
            our_pt = ba.Vec3(spt[0], spt[1], spt[2])
            for otherspawn in [
                    i for i in range(spawn_count)
                    if self._flags[i].team is not player.team
            ]:
                spt = self.map.spawn_by_flag_points[otherspawn]
                their_pt = ba.Vec3(spt[0], spt[1], spt[2])
                dist = (their_pt - our_pt).length()
                if dist < closest_distance:
                    closest_distance = dist
                    closest_spawn = spawn

        pos = self.map.spawn_by_flag_points[closest_spawn]
        x_range = (-0.5, 0.5) if pos[3] == 0.0 else (-pos[3], pos[3])
        z_range = (-0.5, 0.5) if pos[5] == 0.0 else (-pos[5], pos[5])
        pos = (pos[0] + random.uniform(*x_range), pos[1],
               pos[2] + random.uniform(*z_range))
        return pos
예제 #7
0
파일: railgun.py 프로젝트: Dliwk/bs1new
    def __init__(self,
                 position=(0, 5, 0),
                 direction=(0, 2, 0),
                 source_player=None,
                 owner=None,
                 color=(1, 1, 1)) -> None:
        super().__init__()
        self._color = color

        self.node = ba.newnode('light',
                               delegate=self,
                               attrs={
                                   'position': position,
                                   'color': self._color
                               })
        ba.animate(self.node, 'radius', {0: 0, 0.1: 0.5, 0.5: 0})

        self.source_player = source_player
        self.owner = owner
        self._life_timer = ba.Timer(
            0.5, ba.WeakCall(self.handlemessage, ba.DieMessage()))

        pos = position
        vel = tuple(i / 5 for i in ba.Vec3(direction).normalized())
        for _ in range(500):  # Optimization :(
            ba.newnode('explosion',
                       owner=self.node,
                       attrs={
                           'position': pos,
                           'radius': 0.2,
                           'color': self._color
                       })
            pos = (pos[0] + vel[0], pos[1] + vel[1], pos[2] + vel[2])

        for node in _ba.getnodes():
            if node and node.getnodetype() == 'spaz':
                # pylint: disable=invalid-name
                m3 = ba.Vec3(position)
                a = ba.Vec3(direction[2], direction[1], direction[0])
                m1 = ba.Vec3(node.position)
                # pylint: enable=invalid-name
                # distance between node and line
                dist = (a * (m1 - m3)).length() / a.length()
                if dist < 0.3:
                    if node and node != self.owner and node.getdelegate(
                            PlayerSpaz, True).getplayer(
                                ba.Player, True).team != self.owner.team:
                        node.handlemessage(ba.FreezeMessage())
                        pos = self.node.position
                        hit_dir = (0, 10, 0)

                        node.handlemessage(
                            ba.HitMessage(pos=pos,
                                          magnitude=50,
                                          velocity_magnitude=50,
                                          radius=0,
                                          srcnode=self.node,
                                          source_player=self.source_player,
                                          force_direction=hit_dir))
예제 #8
0
    def _spawn_target(self) -> None:

        # Generate a few random points; we'll use whichever one is farthest
        # from our existing targets (don't want overlapping targets).
        points = []

        for _i in range(4):
            # Calc a random point within a circle.
            while True:
                xpos = random.uniform(-1.0, 1.0)
                ypos = random.uniform(-1.0, 1.0)
                if xpos * xpos + ypos * ypos < 1.0:
                    break
            points.append(ba.Vec3(8.0 * xpos, 2.2, -3.5 + 5.0 * ypos))

        def get_min_dist_from_target(pnt: ba.Vec3) -> float:
            return min((t.get_dist_from_point(pnt) for t in self._targets))

        # If we have existing targets, use the point with the highest
        # min-distance-from-targets.
        if self._targets:
            point = max(points, key=get_min_dist_from_target)
        else:
            point = points[0]

        self._targets.append(Target(position=point))
예제 #9
0
    def _update_player_order(self) -> None:
        # FIXME: tidy this up

        # Calc all player distances.
        for player in self.players:
            pos: Optional[ba.Vec3]
            try:
                assert isinstance(player.actor, PlayerSpaz)
                assert player.actor.node
                pos = ba.Vec3(player.actor.node.position)
            except Exception:
                pos = None
            if pos is not None:
                r_index = player.gamedata['last_region']
                rg1 = self._regions[r_index]
                r1pt = ba.Vec3(rg1.pos[:3])
                rg2 = self._regions[0] if r_index == len(
                    self._regions) - 1 else self._regions[r_index + 1]
                r2pt = ba.Vec3(rg2.pos[:3])
                r2dist = (pos - r2pt).length()
                amt = 1.0 - (r2dist / (r2pt - r1pt).length())
                amt = player.gamedata['lap'] + (r_index + amt) * (
                    1.0 / len(self._regions))
                player.gamedata['distance'] = amt

        # Sort players by distance and update their ranks.
        p_list = [[player.gamedata['distance'], player]
                  for player in self.players]

        p_list.sort(reverse=True, key=lambda x: x[0])
        for i, plr in enumerate(p_list):
            try:
                plr[1].gamedata['rank'] = i
                if plr[1].actor is not None:
                    # noinspection PyUnresolvedReferences
                    node = plr[1].actor.distance_txt
                    if node:
                        node.text = str(i + 1) if plr[1].is_alive() else ''
            except Exception:
                ba.print_exception('error updating player orders')
예제 #10
0
            def _update(self):
                if not self.target:
                    del self  # commit suicide because we have no goal in our existing :(
                    return
                d = ba.Vec3(self.target.position) - ba.Vec3(self.position)

                if d.length() < 0.1:
                    self._blast()
                    del self
                    return

                d = d.normalized() * 0.04

                from math import sin, cos

                self.position = (self.position[0] + d.x +
                                 sin(ba.time() * 2) * 0.03,
                                 self.position[1] + d.y, self.position[2] +
                                 d.z + cos(ba.time() * 2) * 0.03)
                self._sparkle()

                ba.timer(0.001, ba.WeakCall(self._update))
예제 #11
0
    def _update_bots(self) -> None:
        bots = self._bots.get_living_bots()
        for bot in bots:
            bot.target_flag = None

        # If we're waiting on a continue, stop here so they don't keep scoring.
        if self.is_waiting_for_continue():
            self._bots.stop_moving()
            return

        # If we've got a flag and no player are holding it, find the closest
        # bot to it, and make them the designated flag-bearer.
        assert self._flag is not None
        if self._flag.node:
            for player in self.players:
                if player.actor:
                    assert isinstance(player.actor, PlayerSpaz)
                    if (player.actor.is_alive() and player.actor.node.hold_node
                            == self._flag.node):
                        return

            flagpos = ba.Vec3(self._flag.node.position)
            closest_bot: Optional[SpazBot] = None
            closest_dist = 0.0  # Always gets assigned first time through.
            for bot in bots:
                # If a bot is picked up, he should forget about the flag.
                if bot.held_count > 0:
                    continue
                assert bot.node
                botpos = ba.Vec3(bot.node.position)
                botdist = (botpos - flagpos).length()
                if closest_bot is None or botdist < closest_dist:
                    closest_bot = bot
                    closest_dist = botdist
            if closest_bot is not None:
                closest_bot.target_flag = self._flag
예제 #12
0
    def __init__(self, position: Sequence[float]):
        self._r1 = 0.45
        self._r2 = 1.1
        self._r3 = 2.0
        self._rfudge = 0.15
        super().__init__()
        self._position = ba.Vec3(position)
        self._hit = False

        # It can be handy to test with this on to make sure the projection
        # isn't too far off from the actual object.
        show_in_space = False
        loc1 = ba.newnode('locator',
                          attrs={
                              'shape': 'circle',
                              'position': position,
                              'color': (0, 1, 0),
                              'opacity': 0.5,
                              'draw_beauty': show_in_space,
                              'additive': True
                          })
        loc2 = ba.newnode('locator',
                          attrs={
                              'shape': 'circleOutline',
                              'position': position,
                              'color': (0, 1, 0),
                              'opacity': 0.3,
                              'draw_beauty': False,
                              'additive': True
                          })
        loc3 = ba.newnode('locator',
                          attrs={
                              'shape': 'circleOutline',
                              'position': position,
                              'color': (0, 1, 0),
                              'opacity': 0.1,
                              'draw_beauty': False,
                              'additive': True
                          })
        self._nodes = [loc1, loc2, loc3]
        ba.animate_array(loc1, 'size', 1, {0: [0.0], 0.2: [self._r1 * 2.0]})
        ba.animate_array(loc2, 'size', 1, {
            0.05: [0.0],
            0.25: [self._r2 * 2.0]
        })
        ba.animate_array(loc3, 'size', 1, {0.1: [0.0], 0.3: [self._r3 * 2.0]})
        ba.playsound(ba.getsound('laserReverse'))
예제 #13
0
파일: rocket.py 프로젝트: Dliwk/bs1new
    def shot(self, spaz: Spaz) -> None:
        """Release a rocket"""
        time = ba.time()
        if time - self.last_shot > 0.6:
            self.last_shot = time
            center = spaz.node.position_center
            forward = spaz.node.position_forward
            direction = [
                center[0] - forward[0], forward[1] - center[1],
                center[2] - forward[2]
            ]
            direction[1] = 0.0

            mag = 10.0 / ba.Vec3(*direction).length()
            vel = [v * mag for v in direction]
            Rocket(position=spaz.node.position,
                   velocity=vel,
                   owner=spaz.getplayer(ba.Player),
                   source_player=spaz.getplayer(ba.Player),
                   color=spaz.node.color).autoretain()
예제 #14
0
    def update_ai(self) -> None:
        """Should be called periodically to update the spaz' AI."""
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-locals
        if self.update_callback is not None:
            if self.update_callback(self):
                # Bot has been handled.
                return

        if not self.node:
            return

        pos = self.node.position
        our_pos = ba.Vec3(pos[0], 0, pos[2])
        can_attack = True

        target_pt_raw: Optional[ba.Vec3]
        target_vel: Optional[ba.Vec3]

        # If we're a flag-bearer, we're pretty simple-minded - just walk
        # towards the flag and try to pick it up.
        if self.target_flag:
            if self.node.hold_node:
                holding_flag = (self.node.hold_node.getnodetype() == 'flag')
            else:
                holding_flag = False

            # If we're holding the flag, just walk left.
            if holding_flag:
                # Just walk left.
                self.node.move_left_right = -1.0
                self.node.move_up_down = 0.0

            # Otherwise try to go pick it up.
            elif self.target_flag.node:
                target_pt_raw = ba.Vec3(*self.target_flag.node.position)
                diff = (target_pt_raw - our_pos)
                diff = ba.Vec3(diff[0], 0, diff[2])  # Don't care about y.
                dist = diff.length()
                to_target = diff.normalized()

                # If we're holding some non-flag item, drop it.
                if self.node.hold_node:
                    self.node.pickup_pressed = True
                    self.node.pickup_pressed = False
                    return

                # If we're a runner, run only when not super-near the flag.
                if self.run and dist > 3.0:
                    self._running = True
                    self.node.run = 1.0
                else:
                    self._running = False
                    self.node.run = 0.0

                self.node.move_left_right = to_target.x
                self.node.move_up_down = -to_target.z
                if dist < 1.25:
                    self.node.pickup_pressed = True
                    self.node.pickup_pressed = False
            return

        # Not a flag-bearer. If we're holding anything but a bomb, drop it.
        if self.node.hold_node:
            holding_bomb = (self.node.hold_node.getnodetype()
                            in ['bomb', 'prop'])
            if not holding_bomb:
                self.node.pickup_pressed = True
                self.node.pickup_pressed = False
                return

        target_pt_raw, target_vel = self._get_target_player_pt()

        if target_pt_raw is None:
            # Use default target if we've got one.
            if self.target_point_default is not None:
                target_pt_raw = self.target_point_default
                target_vel = ba.Vec3(0, 0, 0)
                can_attack = False

            # With no target, we stop moving and drop whatever we're holding.
            else:
                self.node.move_left_right = 0
                self.node.move_up_down = 0
                if self.node.hold_node:
                    self.node.pickup_pressed = True
                    self.node.pickup_pressed = False
                return

        # We don't want height to come into play.
        target_pt_raw[1] = 0.0
        assert target_vel is not None
        target_vel[1] = 0.0

        dist_raw = (target_pt_raw - our_pos).length()

        # Use a point out in front of them as real target.
        # (more out in front the farther from us they are)
        target_pt = (target_pt_raw +
                     target_vel * dist_raw * 0.3 * self._lead_amount)

        diff = (target_pt - our_pos)
        dist = diff.length()
        to_target = diff.normalized()

        if self._mode == 'throw':
            # We can only throw if alive and well.
            if not self._dead and not self.node.knockout:

                assert self._throw_release_time is not None
                time_till_throw = self._throw_release_time - ba.time()

                if not self.node.hold_node:
                    # If we haven't thrown yet, whip out the bomb.
                    if not self._have_dropped_throw_bomb:
                        self.drop_bomb()
                        self._have_dropped_throw_bomb = True

                    # Otherwise our lack of held node means we successfully
                    # released our bomb; lets retreat now.
                    else:
                        self._mode = 'flee'

                # Oh crap, we're holding a bomb; better throw it.
                elif time_till_throw <= 0.0:
                    # Jump and throw.
                    def _safe_pickup(node: ba.Node) -> None:
                        if node and self.node:
                            self.node.pickup_pressed = True
                            self.node.pickup_pressed = False

                    if dist > 5.0:
                        self.node.jump_pressed = True
                        self.node.jump_pressed = False

                        # Throws:
                        ba.timer(0.1, ba.Call(_safe_pickup, self.node))
                    else:
                        # Throws:
                        ba.timer(0.1, ba.Call(_safe_pickup, self.node))

                if self.static:
                    if time_till_throw < 0.3:
                        speed = 1.0
                    elif time_till_throw < 0.7 and dist > 3.0:
                        speed = -1.0  # Whiplash for long throws.
                    else:
                        speed = 0.02
                else:
                    if time_till_throw < 0.7:
                        # Right before throw charge full speed towards target.
                        speed = 1.0
                    else:
                        # Earlier we can hold or move backward for a whiplash.
                        speed = 0.0125
                self.node.move_left_right = to_target.x * speed
                self.node.move_up_down = to_target.z * -1.0 * speed

        elif self._mode == 'charge':
            if random.random() < 0.3:
                self._charge_speed = random.uniform(self.charge_speed_min,
                                                    self.charge_speed_max)

                # If we're a runner we run during charges *except when near
                # an edge (otherwise we tend to fly off easily).
                if self.run and dist_raw > self.run_dist_min:
                    self._lead_amount = 0.3
                    self._running = True
                    self.node.run = 1.0
                else:
                    self._lead_amount = 0.01
                    self._running = False
                    self.node.run = 0.0

            self.node.move_left_right = to_target.x * self._charge_speed
            self.node.move_up_down = to_target.z * -1.0 * self._charge_speed

        elif self._mode == 'wait':
            # Every now and then, aim towards our target.
            # Other than that, just stand there.
            if ba.time(timeformat=ba.TimeFormat.MILLISECONDS) % 1234 < 100:
                self.node.move_left_right = to_target.x * (400.0 / 33000)
                self.node.move_up_down = to_target.z * (-400.0 / 33000)
            else:
                self.node.move_left_right = 0
                self.node.move_up_down = 0

        elif self._mode == 'flee':
            # Even if we're a runner, only run till we get away from our
            # target (if we keep running we tend to run off edges).
            if self.run and dist < 3.0:
                self._running = True
                self.node.run = 1.0
            else:
                self._running = False
                self.node.run = 0.0
            self.node.move_left_right = to_target.x * -1.0
            self.node.move_up_down = to_target.z

        # We might wanna switch states unless we're doing a throw
        # (in which case that's our sole concern).
        if self._mode != 'throw':

            # If we're currently charging, keep track of how far we are
            # from our target. When this value increases it means our charge
            # is over (ran by them or something).
            if self._mode == 'charge':
                if (self._charge_closing_in
                        and self._last_charge_dist < dist < 3.0):
                    self._charge_closing_in = False
                self._last_charge_dist = dist

            # If we have a clean shot, throw!
            if (self.throw_dist_min <= dist < self.throw_dist_max
                    and random.random() < self.throwiness and can_attack):
                self._mode = 'throw'
                self._lead_amount = ((0.4 + random.random() * 0.6)
                                     if dist_raw > 4.0 else
                                     (0.1 + random.random() * 0.4))
                self._have_dropped_throw_bomb = False
                self._throw_release_time = (ba.time() +
                                            (1.0 / self.throw_rate) *
                                            (0.8 + 1.3 * random.random()))

            # If we're static, always charge (which for us means barely move).
            elif self.static:
                self._mode = 'wait'

            # If we're too close to charge (and aren't in the middle of an
            # existing charge) run away.
            elif dist < self.charge_dist_min and not self._charge_closing_in:
                # ..unless we're near an edge, in which case we've got no
                # choice but to charge.
                if self.map.is_point_near_edge(our_pos, self._running):
                    if self._mode != 'charge':
                        self._mode = 'charge'
                        self._lead_amount = 0.2
                        self._charge_closing_in = True
                        self._last_charge_dist = dist
                else:
                    self._mode = 'flee'

            # We're within charging distance, backed against an edge,
            # or farther than our max throw distance.. chaaarge!
            elif (dist < self.charge_dist_max or dist > self.throw_dist_max
                  or self.map.is_point_near_edge(our_pos, self._running)):
                if self._mode != 'charge':
                    self._mode = 'charge'
                    self._lead_amount = 0.01
                    self._charge_closing_in = True
                    self._last_charge_dist = dist

            # We're too close to throw but too far to charge - either run
            # away or just chill if we're near an edge.
            elif dist < self.throw_dist_min:
                # Charge if either we're within charge range or
                # cant retreat to throw.
                self._mode = 'flee'

            # Do some awesome jumps if we're running.
            # FIXME: pylint: disable=too-many-boolean-expressions
            if ((self._running and 1.2 < dist < 2.2
                 and ba.time() - self._last_jump_time > 1.0)
                    or (self.bouncy and ba.time() - self._last_jump_time > 0.4
                        and random.random() < 0.5)):
                self._last_jump_time = ba.time()
                self.node.jump_pressed = True
                self.node.jump_pressed = False

            # Throw punches when real close.
            if dist < (1.6 if self._running else 1.2) and can_attack:
                if random.random() < self.punchiness:
                    self.on_punch_press()
                    self.on_punch_release()
예제 #15
0
 def _on_bot_spawn(self, spaz: SpazBot) -> None:
     # We want to move to the left by default.
     spaz.target_point_default = ba.Vec3(0, 0, 0)
예제 #16
0
 def get_dist_from_point(self, pos: Sequence[float]) -> float:
     """Given a point, returns distance squared from it."""
     return (ba.Vec3(pos) - self._position).length()
예제 #17
0
    def do_hit_at_position(self, pos: Sequence[float],
                           player: ba.Player) -> bool:
        """Handle a bomb hit at the given position."""
        # pylint: disable=too-many-statements
        from bastd.actor import popuptext
        activity = self.activity

        # Ignore hits if the game is over or if we've already been hit
        if activity.has_ended() or self._hit or not self._nodes:
            return False

        diff = (ba.Vec3(pos) - self._position)

        # Disregard Y difference. Our target point probably isn't exactly
        # on the ground anyway.
        diff[1] = 0.0
        dist = diff.length()

        bullseye = False
        if dist <= self._r3 + self._rfudge:
            # Inform our activity that we were hit
            self._hit = True
            activity.handlemessage(self.TargetHitMessage())
            keys: Dict[float, Sequence[float]] = {
                0.0: (1.0, 0.0, 0.0),
                0.049: (1.0, 0.0, 0.0),
                0.05: (1.0, 1.0, 1.0),
                0.1: (0.0, 1.0, 0.0)
            }
            cdull = (0.3, 0.3, 0.3)
            popupcolor: Sequence[float]
            if dist <= self._r1 + self._rfudge:
                bullseye = True
                self._nodes[1].color = cdull
                self._nodes[2].color = cdull
                ba.animate_array(self._nodes[0], 'color', 3, keys, loop=True)
                popupscale = 1.8
                popupcolor = (1, 1, 0, 1)
                streak = player.gamedata['streak']
                points = 10 + min(20, streak * 2)
                ba.playsound(ba.getsound('bellHigh'))
                if streak > 0:
                    ba.playsound(
                        ba.getsound(
                            'orchestraHit4' if streak > 3 else
                            'orchestraHit3' if streak > 2 else
                            'orchestraHit2' if streak > 1 else 'orchestraHit'))
            elif dist <= self._r2 + self._rfudge:
                self._nodes[0].color = cdull
                self._nodes[2].color = cdull
                ba.animate_array(self._nodes[1], 'color', 3, keys, loop=True)
                popupscale = 1.25
                popupcolor = (1, 0.5, 0.2, 1)
                points = 4
                ba.playsound(ba.getsound('bellMed'))
            else:
                self._nodes[0].color = cdull
                self._nodes[1].color = cdull
                ba.animate_array(self._nodes[2], 'color', 3, keys, loop=True)
                popupscale = 1.0
                popupcolor = (0.8, 0.3, 0.3, 1)
                points = 2
                ba.playsound(ba.getsound('bellLow'))

            # Award points/etc.. (technically should probably leave this up
            # to the activity).
            popupstr = '+' + str(points)

            # If there's more than 1 player in the game, include their
            # names and colors so they know who got the hit.
            if len(activity.players) > 1:
                popupcolor = ba.safecolor(player.color, target_intensity=0.75)
                popupstr += ' ' + player.get_name()
            popuptext.PopupText(popupstr,
                                position=self._position,
                                color=popupcolor,
                                scale=popupscale).autoretain()

            # Give this player's team points and update the score-board.
            player.team.gamedata['score'] += points
            assert isinstance(activity, TargetPracticeGame)
            activity.update_scoreboard()

            # Also give this individual player points
            # (only applies in teams mode).
            assert activity.stats is not None
            activity.stats.player_scored(player,
                                         points,
                                         showpoints=False,
                                         screenmessage=False)

            ba.animate_array(self._nodes[0], 'size', 1, {
                0.8: self._nodes[0].size,
                1.0: [0.0]
            })
            ba.animate_array(self._nodes[1], 'size', 1, {
                0.85: self._nodes[1].size,
                1.05: [0.0]
            })
            ba.animate_array(self._nodes[2], 'size', 1, {
                0.9: self._nodes[2].size,
                1.1: [0.0]
            })
            ba.timer(1.1, ba.Call(self.handlemessage, ba.DieMessage()))

        return bullseye