示例#1
0
    def _first_type_handler(self):
        owner_torso_pos = self.owner.torso_position
        position = (owner_torso_pos[0] - 0.25 + random.random() * 0.5,
                    owner_torso_pos[1] - 0.25 + random.random() * 0.5,
                    owner_torso_pos[2] - 0.25 + random.random() * 0.5)

        if self.particle_type in ('sweat', 'spark'):
            spread = 0.1
            scale = random.random() * 0.8
            owner_vel = self.owner.velocity
            vel = 4 if not self.particle_type == 'ice' else 0
            velocity = ((-vel + (random.random() *
                                 (vel * 2))) + owner_vel[0] * 2,
                        (-vel + (random.random() *
                                 (vel * 2))) + owner_vel[1] * 4,
                        (-vel + (random.random() *
                                 (vel * 2))) + owner_vel[2] * 2)
        else:
            spread = 0.15
            velocity = (0, 0, 0)
            scale = random.random() * 0.6

        ba.emitfx(position=position,
                  velocity=velocity,
                  count=10,
                  scale=scale,
                  spread=spread,
                  chunk_type=self.particle_type)
示例#2
0
 def emit_extra_sparks() -> None:
     ba.emitfx(position=position,
               velocity=velocity,
               count=int(10.0 + random.random() * 20),
               scale=0.8,
               spread=1.5,
               chunk_type='spark')
示例#3
0
 def emit() -> None:
     ba.emitfx(position=position,
               velocity=velocity,
               count=int(4.0 + random.random() * 8),
               spread=0.7,
               chunk_type='slime')
     ba.emitfx(position=position,
               velocity=velocity,
               count=int(4.0 + random.random() * 8),
               scale=0.5,
               spread=0.7,
               chunk_type='slime')
     ba.emitfx(position=position,
               velocity=velocity,
               count=15,
               scale=0.6,
               chunk_type='slime',
               emit_type='stickers')
     ba.emitfx(position=position,
               velocity=velocity,
               count=20,
               scale=0.7,
               chunk_type='spark',
               emit_type='stickers')
     ba.emitfx(position=position,
               velocity=velocity,
               count=int(6.0 + random.random() * 12),
               scale=0.8,
               spread=1.5,
               chunk_type='spark')
示例#4
0
 def emit_splinters() -> None:
     ba.emitfx(position=position,
               velocity=velocity,
               count=int(20.0 + random.random() * 25),
               scale=0.8,
               spread=1.0,
               chunk_type='splinter')
示例#5
0
 def _sparkle(self):
     ba.emitfx(position=self.position,
               velocity=(0, 1, 0),
               count=int(random.random() * 5 + 5),
               scale=0.8,
               spread=0.3,
               chunk_type='spark')
    def emitIce(self):
        spaz = self.spazRef()

        if spaz is None or not spaz.is_alive() or not spaz.node.exists():
            self.handlemessage(ba.DieMessage())
            return
        ba.emitfx(position=spaz.node.position , velocity=spaz.node.velocity, count=random.randint(2,8), scale=0.4, spread=0.2, chunk_type="ice")
 def emitDistortion(self):
     spaz = self.spazRef()
     if spaz is None or not spaz.is_alive() or not spaz.node.exists():
         self.handlemessage(ba.DieMessage())
         return
     ba.emitfx(position=spaz.node.position,emit_type="distortion",spread=1.0)
     ba.emitfx(position=spaz.node.position, velocity=spaz.node.velocity,count=random.randint(1,5),emit_type="tendrils",tendril_type="smoke")
示例#8
0
 def on_punch_press(self) -> None:
     """
     Called to 'press punch' on this spaz;
     used for player or AI connections.
     """
     if not self.node or self.frozen or self.node.knockout > 0.0:
         return
     t_ms = ba.time(timeformat=ba.TimeFormat.MILLISECONDS)
     assert isinstance(t_ms, int)
     if t_ms - self.last_punch_time_ms >= self._punch_cooldown:
         if self.punch_callback is not None:
             self.punch_callback(self)
         self._punched_nodes = set()  # Reset this.
         self.last_punch_time_ms = t_ms
         self.node.color = ((0 + random.random() * 6.5),
                            (0 + random.random() * 6.5),
                            (0 + random.random() * 6.5))
         self.node.highlight = ((0 + random.random() * 6.5),
                                (0 + random.random() * 6.5),
                                (0 + random.random() * 6.5))
         ba.emitfx(position=self.node.position,
                   velocity=self.node.velocity,
                   count=35,
                   scale=0.5,
                   spread=2,
                   chunk_type='spark')
         self.node.punch_pressed = True
         if not self.node.hold_node:
             ba.timer(
                 0.1,
                 ba.WeakCall(self._safe_play_sound,
                             SpazFactory.get().swish_sound, 0.8))
     self._turbo_filter_add_press('punch')
示例#9
0
 def emit() -> None:
     ba.emitfx(position=position,
               velocity=velocity,
               count=30,
               spread=2.0,
               scale=0.4,
               chunk_type='ice',
               emit_type='stickers')
示例#10
0
    def project_stand(pos: Sequence[float]) -> None:
        """Project a flag-stand onto the ground at the given position.

        Useful for games such as capture-the-flag to show where a
        movable flag originated from.
        """
        assert len(pos) == 3
        ba.emitfx(position=pos, emit_type='flag_stand')
示例#11
0
    def _second_type_handler(self):
        position = (self.owner.position[0], self.owner.position[1] - 0.25,
                    self.owner.position[2])

        ba.emitfx(position=position,
                  count=10,
                  scale=0.1 + random.random(),
                  spread=0.15,
                  chunk_type=self.particle_type)
示例#12
0
    def _fourth_type_handler(self):
        position = (self.owner.position[0], self.owner.position[1] - 0.5,
                    self.owner.position[2])

        ba.emitfx(position=position,
                  count=10,
                  scale=0.1 + random.random(),
                  spread=0.001,
                  chunk_type=self.particle_type,
                  emit_type='stickers')
示例#13
0
    def wavedash(self) -> None:
        if not self.node:
            return

        isMoving = abs(self.node.move_up_down) >= 0.5 or abs(
            self.node.move_left_right) >= 0.5

        if self._dead or not self.grounded or not isMoving:
            return

        if self.node.knockout > 0.0 or self.frozen or self.node.hold_node:
            return

        t_ms = ba.time(timeformat=ba.TimeFormat.MILLISECONDS)
        assert isinstance(t_ms, int)

        if t_ms - self.last_wavedash_time_ms >= self._wavedash_cooldown:

            move = [self.node.move_left_right, -self.node.move_up_down]
            vel = [self.node.velocity[0], self.node.velocity[2]]

            move_length = math.hypot(move[0], move[1])
            vel_length = math.hypot(vel[0], vel[1])
            if vel_length < 1.25: return
            move_norm = [m / move_length for m in move]
            vel_norm = [v / vel_length for v in vel]
            dot = sum(x * y for x, y in zip(move_norm, vel_norm))
            turn_power = min(round(math.acos(dot) / math.pi, 2) * 1.3, 1)
            if turn_power < 0.2: return

            boost_power = math.sqrt(math.pow(vel[0], 2) +
                                    math.pow(vel[1], 2)) * 1.2
            boost_power = min(pow(boost_power, 4), 160)
            #print(boost_power * turn_power)

            self.last_wavedash_time_ms = t_ms

            # FX
            ba.emitfx(position=self.node.position,
                      velocity=(vel[0] * 0.5, -1, vel[1] * 0.5),
                      chunk_type='sweat',
                      count=8,
                      scale=boost_power / 160 * turn_power,
                      spread=0.25)

            # Boost itself
            pos = self.node.position
            for i in range(6):
                self.node.handlemessage('impulse', pos[0],
                                        -0.1 + pos[1] + i * 0.1, pos[2], 0, 0,
                                        0, boost_power * turn_power,
                                        boost_power * turn_power, 0, 0,
                                        move[0], 0, move[1])
示例#14
0
def airstrike_blast(self: stdbomb.Blast, position, velocity, blast_radius,
                    hit_type, hit_subtype):
    self.node = None  # We must define node
    ba.emitfx(
        position=position,
        velocity=velocity,
        count=75,
        spread=0.7,
        chunk_type='spark')

    Airstrike(position=position, source_player=ba.existing(self._source_player)).autoretain()
    ba.playsound(ba.getsound('laserReverse'))  # position=self.node.position?
示例#15
0
    def _third_type_handler(self):
        sin = math.sin(self._offset) * self._radius
        cos = math.cos(self._offset) * self._radius
        self._offset += 0.1
        position = (self.owner.position[0] + cos, self.owner.position[1],
                    self.owner.position[2] + sin)

        ba.emitfx(position=position,
                  count=5,
                  scale=1,
                  spread=0,
                  chunk_type=self.particle_type)
示例#16
0
 def smoke(x: int, y: int, z: int) -> None:
     ba.emitfx(position=(x, z, y),
               velocity=(0, 2, 0),
               count=1,
               emit_type='tendrils',
               tendril_type='smoke')
     ba.emitfx(position=(x, z, y),
               velocity=(0, 2, 0),
               count=int(1.0 + random.random() * 2),
               scale=0.8,
               spread=1.5,
               chunk_type='spark')
示例#17
0
    def _jetpack_wrapper():
        if not self.node.exists():
            return

        if self.node.knockout <= 0 and self.node.frozen <= 0:
            self.node.jump_pressed = True
            ba.emitfx(position=(self.node.position[0],
                                self.node.position[1] - 0.5,
                                self.node.position[2]),
                      velocity=(0, -10, 0),
                      count=75,
                      spread=0.25,
                      chunk_type='spark')
示例#18
0
def heal_blast(self: stdbomb.Blast,
               position: Sequence[float] = (0.0, 1.0, 0.0),
               velocity: Sequence[float] = (0.0, 0.0, 0.0),
               blast_radius: float = 2.0,
               hit_type: str = 'explosion',
               hit_subtype: str = 'normal'):
    self.node = None
    ba.emitfx(
        position=position,
        velocity=(0, 0, 0),
        count=75,
        spread=0.7,
        chunk_type='spark')

    TreatmentArea(position=position).autoretain()
    ba.playsound(ba.getsound('healthPowerup'), position=position)
示例#19
0
文件: rocket.py 项目: Dliwk/bs1new
 def emit(self) -> None:
     """Emit a trace after rocket"""
     ba.emitfx(position=self.node.position,
               scale=0.4,
               spread=0.01,
               chunk_type='spark')
     if not self.node:
         return
     self.node.position = (self.node.position[0], self.base_pos_y,
                           self.node.position[2])  # ignore y
     ba.newnode('explosion',
                owner=self.node,
                attrs={
                    'position': self.node.position,
                    'radius': 0.2,
                    'color': self._color
                })
示例#20
0
 def regen():
     if (msg is not None and msg.node.exists() and
             msg.node.getdelegate(playerspaz.PlayerSpaz).hitpoints <
             msg.node.getdelegate(
                 playerspaz.PlayerSpaz).hitpoints_max):
         msg.node.getdelegate(playerspaz.PlayerSpaz).hitpoints += 1
         msg.node.getdelegate(
             playerspaz.PlayerSpaz)._last_hit_time = None
         msg.node.getdelegate(
             playerspaz.PlayerSpaz)._num_time_shit = 0
         msg.node.hurt -= 0.001
         ba.emitfx(position=msg.node.position,
                   velocity=(0, 3, 0),
                   count=int(3.0 + random.random() * 5),
                   scale=1.5,
                   spread=0.3,
                   chunk_type='sweat')
     else:
         self.regen_timer = None
示例#21
0
def emit_snowfall(self):
    bounds = self.map.get_def_bound_box('map_bounds') if getattr(
        self, 'map', None) else (
            -14.3569491031, -2.200293481, -18.507121877, 14.8787058369,
            11.999620949, 11.419771563000001)  # The Pad bounds (for main menu)

    position = (random.uniform(bounds[0], bounds[3]),
                random.uniform(bounds[4] * 1.15, bounds[4] * 1.45),
                random.uniform(bounds[2], bounds[5]))

    vel1 = ((-5.0 + random.random() * 30.0) *
            (-1.0 if position[0] > 0 else 1.0))

    vel = (vel1, -50.0, random.uniform(-20, 20))

    ba.emitfx(position=position,
              velocity=vel,
              count=5,
              scale=0.4 + random.random(), #0.4
              spread=0,
              chunk_type='spark')
示例#22
0
            def emit() -> None:
                if self.blast_type != 'tnt':
                    ba.emitfx(position=position,
                              velocity=velocity,
                              count=int(4.0 + random.random() * 8),
                              chunk_type='rock')
                    ba.emitfx(position=position,
                              velocity=velocity,
                              count=int(4.0 + random.random() * 8),
                              scale=0.5,
                              chunk_type='rock')
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=30,
                          scale=1.0 if self.blast_type == 'tnt' else 0.7,
                          chunk_type='spark',
                          emit_type='stickers')
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=int(18.0 + random.random() * 20),
                          scale=1.0 if self.blast_type == 'tnt' else 0.8,
                          spread=1.5,
                          chunk_type='spark')

                # tnt throws splintery chunks
                if self.blast_type == 'tnt':

                    def emit_splinters() -> None:
                        ba.emitfx(position=position,
                                  velocity=velocity,
                                  count=int(20.0 + random.random() * 25),
                                  scale=0.8,
                                  spread=1.0,
                                  chunk_type='splinter')

                    ba.timer(0.01, emit_splinters)

                # every now and then do a sparky one
                if self.blast_type == 'tnt' or random.random() < 0.1:

                    def emit_extra_sparks() -> None:
                        ba.emitfx(position=position,
                                  velocity=velocity,
                                  count=int(10.0 + random.random() * 20),
                                  scale=0.8,
                                  spread=1.5,
                                  chunk_type='spark')

                    ba.timer(0.02, emit_extra_sparks)
示例#23
0
 def emit() -> None:
     ba.emitfx(position=position,
               velocity=velocity,
               count=int(4.0 + random.random() * 8),
               scale=0.8,
               chunk_type='metal')
     ba.emitfx(position=position,
               velocity=velocity,
               count=int(4.0 + random.random() * 8),
               scale=0.4,
               chunk_type='metal')
     ba.emitfx(position=position,
               velocity=velocity,
               count=20,
               scale=0.7,
               chunk_type='spark',
               emit_type='stickers')
     ba.emitfx(position=position,
               velocity=velocity,
               count=int(8.0 + random.random() * 15),
               scale=0.8,
               spread=1.5,
               chunk_type='spark')
示例#24
0
    def handlemessage(self, msg, returned):
        from bastd.actor.popuptext import PopupText
        if isinstance(msg, ba.HitMessage):
            if msg.hit_type == 'punch':
                if not self.node:
                    return None
                if self.node.invincible:
                    return True
                mag = msg.magnitude * self.impact_scale
                velocity_mag = msg.velocity_magnitude * self.impact_scale
                damage_scale = 0.22

                # If they've got a shield, deliver it to that instead.
                if self.shield:
                    if msg.flat_damage:
                        damage = msg.flat_damage * self.impact_scale
                    else:
                        damage = damage_scale * self.node.damage

                    # Its a cleaner event if a hit just kills the shield
                    # without damaging the player.
                    # However, massive damage events should still be able to
                    # damage the player. This hopefully gives us a happy medium.
                    max_spillover = stdspaz.get_factory(
                    ).max_shield_spillover_damage

                    # If they passed our spillover threshold,
                    # pass damage along to spaz.
                    if self.shield_hitpoints <= -max_spillover:
                        leftover_damage = -max_spillover - self.shield_hitpoints
                        shield_leftover_ratio = leftover_damage / damage

                        # Scale down the magnitudes applied to spaz accordingly.
                        mag *= shield_leftover_ratio
                        velocity_mag *= shield_leftover_ratio
                    else:
                        return True  # Good job shield!
                else:
                    shield_leftover_ratio = 1.0

                if msg.flat_damage:
                    damage = int(msg.flat_damage * self.impact_scale *
                                 shield_leftover_ratio)
                else:
                    damage = int(damage_scale * self.node.damage)

                if damage > 999:
                    PopupText(get_locale('fatality_text'),
                              color=(0.905, 0.298, 0.235),
                              scale=2.0,
                              position=self.node.position).autoretain()

                    ba.emitfx(position=msg.pos,
                              chunk_type='spark',
                              velocity=(msg.force_direction[0] * 1.3,
                                        msg.force_direction[1] * 1.3 + 5.0,
                                        msg.force_direction[2] * 1.3),
                              count=45,
                              scale=1.0,
                              spread=1.0)

                    self.lightning_bolt(position=self.node.position, radius=3)

                    gnode = self.activity.globalsnode

                    if not gnode.slow_motion:
                        gnode.slow_motion = True

                        def off_sm():
                            if gnode:
                                gnode.slow_motion = False

                        ba.timer(0.5, off_sm)

                elif 800 < damage < 999:
                    PopupText(get_locale('crazy_text'),
                              color=(0.180, 0.800, 0.443),
                              scale=1.5,
                              position=self.node.position).autoretain()

                    ba.emitfx(position=msg.pos,
                              chunk_type='spark',
                              velocity=(msg.force_direction[0] * 1.3,
                                        msg.force_direction[1] * 1.3 + 5.0,
                                        msg.force_direction[2] * 1.3),
                              count=45,
                              scale=1.0,
                              spread=1.0)
                elif 750 < damage < 800:
                    PopupText(get_locale('aggressive_text'),
                              color=(0.231, 0.596, 0.858),
                              scale=1.5,
                              position=self.node.position).autoretain()
        return returned
示例#25
0
def lucky_block_callback(self: stdspaz.Spaz, msg: ba.PowerupMessage):
    event_number = random.randint(1, 15)

    if event_number in (1, 2, 3):
        powerup_type = stdpowerup.PowerupBoxFactory().get_random_powerup_type()

        self.node.handlemessage(ba.PowerupMessage(poweruptype=powerup_type))
    elif event_number == 4:
        ba.camerashake(1)
        ba.emitfx(position=(self.node.position[0], self.node.position[1] + 4,
                            self.node.position[2]),
                  velocity=(0, 0, 0),
                  count=700,
                  spread=0.7,
                  chunk_type='spark')

        powerup_type = stdpowerup.PowerupBoxFactory.get(
        ).get_random_powerup_type()

        stdpowerup.PowerupBox(position=(self.node.position[0],
                                        self.node.position[1] + 4,
                                        self.node.position[2]),
                              poweruptype=powerup_type,
                              expire=True).autoretain()

        powerup_type = stdpowerup.PowerupBoxFactory.get(
        ).get_random_powerup_type()

        stdpowerup.PowerupBox(position=(self.node.position[0],
                                        self.node.position[1] + 4,
                                        self.node.position[2]),
                              poweruptype=powerup_type,
                              expire=True).autoretain()

        powerup_type = stdpowerup.PowerupBoxFactory.get(
        ).get_random_powerup_type()

        stdpowerup.PowerupBox(position=(self.node.position[0],
                                        self.node.position[1] + 4,
                                        self.node.position[2]),
                              poweruptype=powerup_type,
                              expire=True).autoretain()
    elif event_number == 5:
        stdbomb.Bomb(position=(self.node.position[0],
                               self.node.position[1] + 3,
                               self.node.position[2]),
                     source_player=self.source_player,
                     owner=self.node,
                     blast_radius=6).autoretain()
    elif event_number == 6:
        self.node.handlemessage(ba.FreezeMessage())
    elif event_number == 7:
        chunk_type = ('ice', 'rock', 'metal', 'spark', 'splinter', 'slime')

        ba.emitfx(position=self.node.position,
                  velocity=(random.random() * 2, random.random() * 2,
                            random.random() * 2),
                  count=600,
                  spread=random.random(),
                  chunk_type=random.choice(chunk_type))

        ba.camerashake(1)
        ba.playsound(ba.getsound('corkPop'))  # position=self.node.position?
    elif event_number == 8:
        position = self.node.position

        def rain_wrapper():
            p_type = stdpowerup.PowerupBoxFactory.get(
            ).get_random_powerup_type()

            new_position = (-10 + position[0] + random.random() * 20,
                            position[1] + 6,
                            -10 + position[2] + random.random() * 20)

            stdpowerup.PowerupBox(poweruptype=p_type,
                                  position=new_position).autoretain()

            if random.random() > 0.04:
                ba.timer(0.1, rain_wrapper)

        rain_wrapper()
    elif event_number == 9:
        stdbomb.Blast(position=self.node.position,
                      velocity=self.node.velocity,
                      blast_radius=1.0,
                      blast_type='normal',
                      source_player=None,
                      hit_type='punch',
                      hit_subtype='normal')
    elif event_number == 10:

        def blast(x: int, y: int, z: int) -> None:
            # add sound
            ba.NodeActor(node=ba.newnode('scorch',
                                         attrs={
                                             'position': (x, z, y),
                                             'size': 0.2,
                                             'big': False,
                                         })).autoretain()

        def smoke(x: int, y: int, z: int) -> None:
            ba.emitfx(position=(x, z, y),
                      velocity=(0, 2, 0),
                      count=1,
                      emit_type='tendrils',
                      tendril_type='smoke')
            ba.emitfx(position=(x, z, y),
                      velocity=(0, 2, 0),
                      count=int(1.0 + random.random() * 2),
                      scale=0.8,
                      spread=1.5,
                      chunk_type='spark')

        star_positions = [
            (2, 0),
            (0, 2),
            (-1.2, -1.6),
            (1.82, 0.83),
            (-1.83, 0.82),
            (1.23, -1.57),
            (-1.25, 1.56),
            (-0.65, 1.89),
            (0.82, 1.82),
            (1.27, 1.55),
            (1.82, -0.84),
            (0.31, -1.98),
            (-0.42, -1.96),
            (-1.75, -0.96),
            (-2, -0.14),
            (-0.69, -0.07),
            (-0.39, 0.82),
            (0.41, 0.82),
            (0.71, -0.06),
            (0.01, -0.62),
            (-0.99, 0.82),
            (-1.26, 0.37),
            (-0.89, -0.65),
            (-0.52, -1.05),
            (0.59, -1.07),
            (0.96, -0.8),
            (1.22, 0.35),
            (1.07, 0.82),
            (0.21, 1.39),
            (-0.17, 1.48),
            # ---
            (-1.94, 0.47),
            (-1.51, 1.31),
            (-0.95, 1.76),
            (-0.38, 1.96),
            (0.45, 1.95),
            (1.05, 1.7),
            (1.57, 1.24),
            (1.94, 0.49),
            (1.96, -0.42),
            (1.62, -1.17),
            (0.84, -1.82),
            (-0.78, -1.84),
            (-1.5, -1.33),
            (-1.91, -0.59),
            (-1.99, 0.17),
            (-1, 0.17),
            (-0.7, 0.82),
            (-0.27, 1.19),
            (0.29, 1.15),
            (0.77, 0.82),
            (1, 0.17),
            (0.84, -0.42),
            (0.31, -0.85),
            (-0.8, -1.27),
            (-1, -1),
            (-0.56, 0.33),
            (-0.47, 0.61),
            (0.52, 0.51),
            (-0.1, 0.82),
            (0.13, 0.82),
            (0.6, 0.27),
            (0.46, -0.27),
            (0.29, -0.4),
            (-0.44, -0.27),
            (-0.24, -0.42),
            (-1.36, 0.82),
            (-1.53, 0.59),
            (1.35, 0.83),
            (1.55, 0.61),
            (0.85, -1.28),
            (1.08, -1.13),
            (0.78, -0.34),
            (-0.21, -0.8),
            (0.11, 1.68)
        ]

        class Sparkling(ba.Actor):
            def __init__(self, position: Sequence[float], target: ba.Node):
                super().__init__()
                # nah; nodes not needed
                self.position = position
                self.position = (self.position[0], self.position[1] + 0.5,
                                 self.position[2])
                self.target = target

                ba.timer(0.001, ba.WeakCall(self._update))

            def _sparkle(self):
                ba.emitfx(position=self.position,
                          velocity=(0, 1, 0),
                          count=int(random.random() * 5 + 5),
                          scale=0.8,
                          spread=0.3,
                          chunk_type='spark')

            def _blast(self):
                stdbomb.Blast(position=self.position,
                              velocity=self.target.velocity,
                              blast_radius=2,
                              blast_type='normal',
                              source_player=None,
                              hit_type='punch',
                              hit_subtype='normal').autoretain()

            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))

        def sparkling(x, y, z):
            Sparkling(position=(x, z, y), target=self.node).autoretain()

        def summon_tnt(x, y, z):
            stdbomb.Bomb(bomb_type='tnt',
                         blast_radius=3,
                         position=(x, z + 4, y),
                         velocity=(0, -10, 0)).autoretain()

        scale = 1
        delta = 0.02

        op = self.node.position
        for i, (x, y) in enumerate(star_positions):
            ba.timer(
                i * delta,
                ba.Call(blast, self.node.position[0] + x * scale,
                        self.node.position[2] + y * scale,
                        self.node.position[1]))
        for i in range(4):
            ba.timer((len(star_positions)) * delta + i * 0.2,
                     ba.Call(summon_tnt, op[0], op[2], op[1]))
        ba.timer((len(star_positions)) * delta + 1.0,
                 ba.Call(sparkling, self.node.position[0],
                         self.node.position[2], self.node.position[1]))

        def last_blast():
            stdbomb.Blast(position=self.node.position,
                          velocity=(self.node.velocity[0],
                                    self.node.velocity[1] + 10,
                                    self.node.velocity[2]),
                          blast_radius=2,
                          blast_type='normal',
                          source_player=None,
                          hit_type='punch',
                          hit_subtype='normal').autoretain()

        # ba.timer(
        #     2 * len(star_positions) * delta + 0.2,
        #     last_blast)

    elif event_number == 11:
        offset = -15
        case = 1 if random.random() < 0.5 else -1
        while offset < 15:
            velocity = (case * (12 + 8 * random.random()), -0.1, 0)
            stdbomb.Bomb(bomb_type='tnt',
                         position=(self.node.position[0] - case * 10,
                                   self.node.position[1] + 3,
                                   self.node.position[2] + offset),
                         velocity=velocity).autoretain()

            offset += 1.5
    elif event_number == 12:
        color = {
            0.0: (0, 0, 3),
            0.5: (0, 3, 0),
            1.0: (3, 0, 0),
            1.5: (0, 0, 3)
        }

        # FIXME
        # ba.animate_array(self.node, 'color', 3, color, True)
        # self.node.handlemessage('celebrate', 100000000)
    elif event_number == 13:
        CompanionCube(position=(self.node.position[0],
                                self.node.position[1] + 1,
                                self.node.position[2]),
                      velocity=(0, 10, 0)).autoretain()
    elif event_number == 14:
        ba.emitfx(position=self.node.position,
                  count=100,
                  emit_type='tendrils',
                  tendril_type='smoke')

    elif event_number == 15:

        def drop_man():
            botset: stdbot.SpazBotSet
            activity = ba.getactivity()
            if not hasattr(activity, 'botset'):
                activity.botset = botset = stdbot.SpazBotSet()
            botset = activity.botset
            aoi_bounds = self.activity.globalsnode.area_of_interest_bounds
            botset.spawn_bot(
                stdbot.BrawlerBotLite,
                (random.randrange(int(aoi_bounds[0]),
                                  int(aoi_bounds[3]) + 1), aoi_bounds[4] - 1,
                 random.randrange(int(aoi_bounds[2]),
                                  int(aoi_bounds[5]) + 1)),
                spawn_time=0.001)

        def lightning_bolt(position, radius=1):
            ba.camerashake(4)
            vignette_outer = self.activity.globalsnode.vignette_outer
            # if ba.getactivity().tint is None:
            #     ba.getactivity().std_tint = ba.sharedobj('globals').vignette_outer
            #     vignette_outer = ba.sharedobj('globals').vignette_outer
            # else:
            #     vignette_outer = ba.getactivity().tint

            light = ba.newnode('light',
                               attrs={
                                   'position': position,
                                   'color': (0.4, 0.4, 0.8),
                                   'volume_intensity_scale': 1.0,
                                   'radius': radius
                               })

            ba.animate(light,
                       'intensity', {
                           0: 1,
                           50: radius,
                           150: radius / 2,
                           250: 0,
                           260: radius,
                           410: radius / 2,
                           510: 0
                       },
                       timeformat=ba.TimeFormat.MILLISECONDS,
                       suppress_format_warning=True)

            ba.animate_array(self.activity.globalsnode, 'vignette_outer', 3, {
                0: vignette_outer,
                0.2: (0.2, 0.2, 0.2),
                0.51: vignette_outer
            })

            # ba.playsound(
            #     ba.getsound('grom'),
            #     volume=10,
            #     position=(0, 10, 0))

        lightning_bolt(self.node.position)

        for time in range(15):
            ba.timer(time, drop_man)
示例#26
0
def lucky_block_callback(self: stdspaz.Spaz, msg: ba.PowerupMessage):
    event_number = random.randint(1, 15)

    if event_number in (1, 2, 3):
        powerup_type = stdpowerup.PowerupBoxFactory().get_random_powerup_type()

        self.node.handlemessage(ba.PowerupMessage(poweruptype=powerup_type))
    elif event_number == 4:
        ba.camerashake(1)
        ba.emitfx(position=(self.node.position[0], self.node.position[1] + 4,
                            self.node.position[2]),
                  velocity=(0, 0, 0),
                  count=700,
                  spread=0.7,
                  chunk_type='spark')

        powerup_type = stdpowerup.PowerupBoxFactory.get(
        ).get_random_powerup_type()

        stdpowerup.PowerupBox(position=(self.node.position[0],
                                        self.node.position[1] + 4,
                                        self.node.position[2]),
                              poweruptype=powerup_type,
                              expire=True).autoretain()

        powerup_type = stdpowerup.PowerupBoxFactory.get(
        ).get_random_powerup_type()

        stdpowerup.PowerupBox(position=(self.node.position[0],
                                        self.node.position[1] + 4,
                                        self.node.position[2]),
                              poweruptype=powerup_type,
                              expire=True).autoretain()

        powerup_type = stdpowerup.PowerupBoxFactory.get(
        ).get_random_powerup_type()

        stdpowerup.PowerupBox(position=(self.node.position[0],
                                        self.node.position[1] + 4,
                                        self.node.position[2]),
                              poweruptype=powerup_type,
                              expire=True).autoretain()
    elif event_number == 5:
        stdbomb.Bomb(position=(self.node.position[0],
                               self.node.position[1] + 3,
                               self.node.position[2]),
                     source_player=self.source_player,
                     owner=self.node,
                     blast_radius=6).autoretain()
    elif event_number == 6:
        self.node.handlemessage(ba.FreezeMessage())
    elif event_number == 7:
        chunk_type = ('ice', 'rock', 'metal', 'spark', 'splinter', 'slime')

        ba.emitfx(position=self.node.position,
                  velocity=(random.random() * 2, random.random() * 2,
                            random.random() * 2),
                  count=600,
                  spread=random.random(),
                  chunk_type=random.choice(chunk_type))

        ba.camerashake(1)
        ba.playsound(ba.getsound('corkPop'))  # position=self.node.position?
    elif event_number == 8:
        position = self.node.position

        def rain_wrapper():
            p_type = stdpowerup.PowerupBoxFactory.get(
            ).get_random_powerup_type()

            new_position = (-10 + position[0] + random.random() * 20,
                            position[1] + 6,
                            -10 + position[2] + random.random() * 20)

            stdpowerup.PowerupBox(poweruptype=p_type,
                                  position=new_position).autoretain()

            if random.random() > 0.04:
                ba.timer(0.1, rain_wrapper)

        rain_wrapper()
    elif event_number == 9:
        stdbomb.Blast(position=self.node.position,
                      velocity=self.node.velocity,
                      blast_radius=1.0,
                      blast_type='normal',
                      source_player=None,
                      hit_type='punch',
                      hit_subtype='normal')
    elif event_number == 10:  # Blast-square under spaz
        x = self.node.position[0] - 2
        while x < self.node.position[0] + 2:
            y = self.node.position[2] - 2
            while y < self.node.position[2] + 2:
                stdbomb.Blast(position=(x, self.node.position[1], y),
                              velocity=(self.node.velocity[0],
                                        self.node.velocity[1] + 10,
                                        self.node.velocity[2]),
                              blast_radius=0.5,
                              blast_type='normal',
                              source_player=None,
                              hit_type='punch',
                              hit_subtype='normal')

                y += 1

            x += 1
    elif event_number == 11:
        offset = -15
        case = 1 if random.random() < 0.5 else -1
        while offset < 15:
            velocity = (case * (12 + 8 * random.random()), -0.1, 0)
            stdbomb.Bomb(bomb_type='tnt',
                         position=(self.node.position[0] - case * 10,
                                   self.node.position[1] + 3,
                                   self.node.position[2] + offset),
                         velocity=velocity).autoretain()

            offset += 1.5
    elif event_number == 12:
        color = {
            0.0: (0, 0, 3),
            0.5: (0, 3, 0),
            1.0: (3, 0, 0),
            1.5: (0, 0, 3)
        }

        # FIXME
        # ba.animate_array(self.node, 'color', 3, color, True)
        # self.node.handlemessage('celebrate', 100000000)
    elif event_number == 13:
        CompanionCube(position=(self.node.position[0],
                                self.node.position[1] + 1,
                                self.node.position[2]),
                      velocity=(0, 10, 0)).autoretain()
    elif event_number == 14:
        ba.emitfx(position=self.node.position,
                  count=100,
                  emit_type='tendrils',
                  tendril_type='smoke')

    elif event_number == 15:

        def drop_man():
            botset: stdbot.SpazBotSet
            activity = ba.getactivity()
            if not hasattr(activity, 'botset'):
                activity.botset = botset = stdbot.SpazBotSet()
            botset = activity.botset
            aoi_bounds = self.activity.globalsnode.area_of_interest_bounds
            botset.spawn_bot(
                stdbot.BrawlerBotLite,
                (random.randrange(int(aoi_bounds[0]),
                                  int(aoi_bounds[3]) + 1), aoi_bounds[4] - 1,
                 random.randrange(int(aoi_bounds[2]),
                                  int(aoi_bounds[5]) + 1)),
                spawn_time=0.001)

        def lightning_bolt(position, radius=1):
            ba.camerashake(4)
            vignette_outer = self.activity.globalsnode.vignette_outer
            # if ba.getactivity().tint is None:
            #     ba.getactivity().std_tint = ba.sharedobj('globals').vignette_outer
            #     vignette_outer = ba.sharedobj('globals').vignette_outer
            # else:
            #     vignette_outer = ba.getactivity().tint

            light = ba.newnode('light',
                               attrs={
                                   'position': position,
                                   'color': (0.4, 0.4, 0.8),
                                   'volume_intensity_scale': 1.0,
                                   'radius': radius
                               })

            ba.animate(light,
                       'intensity', {
                           0: 1,
                           50: radius,
                           150: radius / 2,
                           250: 0,
                           260: radius,
                           410: radius / 2,
                           510: 0
                       },
                       timeformat=ba.TimeFormat.MILLISECONDS,
                       suppress_format_warning=True)

            ba.animate_array(self.activity.globalsnode, 'vignette_outer', 3, {
                0: vignette_outer,
                0.2: (0.2, 0.2, 0.2),
                0.51: vignette_outer
            })

            # ba.playsound(
            #     ba.getsound('grom'),
            #     volume=10,
            #     position=(0, 10, 0))

        lightning_bolt(self.node.position)

        for time in range(15):
            ba.timer(time, drop_man)
示例#27
0
    def __init__(self,
                 position: Sequence[float] = (0.0, 1.0, 0.0),
                 velocity: Sequence[float] = (0.0, 0.0, 0.0),
                 blast_radius: float = 2.0,
                 blast_type: str = 'normal',
                 source_player: ba.Player = None,
                 hit_type: str = 'explosion',
                 hit_subtype: str = 'normal'):
        """Instantiate with given values."""

        # bah; get off my lawn!
        # pylint: disable=too-many-locals
        # pylint: disable=too-many-statements

        super().__init__()

        factory = get_factory()

        self.blast_type = blast_type
        self.source_player = source_player
        self.hit_type = hit_type
        self.hit_subtype = hit_subtype
        self.radius = blast_radius

        # set our position a bit lower so we throw more things upward
        rmats = (factory.blast_material, ba.sharedobj('attack_material'))
        self.node = ba.newnode('region',
                               delegate=self,
                               attrs={
                                   'position': (position[0], position[1] - 0.1,
                                                position[2]),
                                   'scale':
                                   (self.radius, self.radius, self.radius),
                                   'type':
                                   'sphere',
                                   'materials':
                                   rmats
                               })

        ba.timer(0.05, self.node.delete)

        # throw in an explosion and flash
        evel = (velocity[0], max(-1.0, velocity[1]), velocity[2])
        explosion = ba.newnode('explosion',
                               attrs={
                                   'position': position,
                                   'velocity': evel,
                                   'radius': self.radius,
                                   'big': (self.blast_type == 'tnt')
                               })
        if self.blast_type == 'ice':
            explosion.color = (0, 0.05, 0.4)

        ba.timer(1.0, explosion.delete)

        if self.blast_type != 'ice':
            ba.emitfx(position=position,
                      velocity=velocity,
                      count=int(1.0 + random.random() * 4),
                      emit_type='tendrils',
                      tendril_type='thin_smoke')
        ba.emitfx(position=position,
                  velocity=velocity,
                  count=int(4.0 + random.random() * 4),
                  emit_type='tendrils',
                  tendril_type='ice' if self.blast_type == 'ice' else 'smoke')
        ba.emitfx(position=position,
                  emit_type='distortion',
                  spread=1.0 if self.blast_type == 'tnt' else 2.0)

        # and emit some shrapnel..
        if self.blast_type == 'ice':

            def emit() -> None:
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=30,
                          spread=2.0,
                          scale=0.4,
                          chunk_type='ice',
                          emit_type='stickers')

            # looks better if we delay a bit
            ba.timer(0.05, emit)

        elif self.blast_type == 'sticky':

            def emit() -> None:
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=int(4.0 + random.random() * 8),
                          spread=0.7,
                          chunk_type='slime')
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=int(4.0 + random.random() * 8),
                          scale=0.5,
                          spread=0.7,
                          chunk_type='slime')
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=15,
                          scale=0.6,
                          chunk_type='slime',
                          emit_type='stickers')
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=20,
                          scale=0.7,
                          chunk_type='spark',
                          emit_type='stickers')
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=int(6.0 + random.random() * 12),
                          scale=0.8,
                          spread=1.5,
                          chunk_type='spark')

            # looks better if we delay a bit
            ba.timer(0.05, emit)

        elif self.blast_type == 'impact':  # regular bomb shrapnel

            def emit() -> None:
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=int(4.0 + random.random() * 8),
                          scale=0.8,
                          chunk_type='metal')
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=int(4.0 + random.random() * 8),
                          scale=0.4,
                          chunk_type='metal')
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=20,
                          scale=0.7,
                          chunk_type='spark',
                          emit_type='stickers')
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=int(8.0 + random.random() * 15),
                          scale=0.8,
                          spread=1.5,
                          chunk_type='spark')

            # looks better if we delay a bit
            ba.timer(0.05, emit)

        else:  # regular or land mine bomb shrapnel

            def emit() -> None:
                if self.blast_type != 'tnt':
                    ba.emitfx(position=position,
                              velocity=velocity,
                              count=int(4.0 + random.random() * 8),
                              chunk_type='rock')
                    ba.emitfx(position=position,
                              velocity=velocity,
                              count=int(4.0 + random.random() * 8),
                              scale=0.5,
                              chunk_type='rock')
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=30,
                          scale=1.0 if self.blast_type == 'tnt' else 0.7,
                          chunk_type='spark',
                          emit_type='stickers')
                ba.emitfx(position=position,
                          velocity=velocity,
                          count=int(18.0 + random.random() * 20),
                          scale=1.0 if self.blast_type == 'tnt' else 0.8,
                          spread=1.5,
                          chunk_type='spark')

                # tnt throws splintery chunks
                if self.blast_type == 'tnt':

                    def emit_splinters() -> None:
                        ba.emitfx(position=position,
                                  velocity=velocity,
                                  count=int(20.0 + random.random() * 25),
                                  scale=0.8,
                                  spread=1.0,
                                  chunk_type='splinter')

                    ba.timer(0.01, emit_splinters)

                # every now and then do a sparky one
                if self.blast_type == 'tnt' or random.random() < 0.1:

                    def emit_extra_sparks() -> None:
                        ba.emitfx(position=position,
                                  velocity=velocity,
                                  count=int(10.0 + random.random() * 20),
                                  scale=0.8,
                                  spread=1.5,
                                  chunk_type='spark')

                    ba.timer(0.02, emit_extra_sparks)

            # looks better if we delay a bit
            ba.timer(0.05, emit)

        lcolor = ((0.6, 0.6, 1.0) if self.blast_type == 'ice' else
                  (1, 0.3, 0.1))
        light = ba.newnode('light',
                           attrs={
                               'position': position,
                               'volume_intensity_scale': 10.0,
                               'color': lcolor
                           })

        scl = random.uniform(0.6, 0.9)
        scorch_radius = light_radius = self.radius
        if self.blast_type == 'tnt':
            light_radius *= 1.4
            scorch_radius *= 1.15
            scl *= 3.0

        iscale = 1.6
        ba.animate(
            light, 'intensity', {
                0: 2.0 * iscale,
                scl * 0.02: 0.1 * iscale,
                scl * 0.025: 0.2 * iscale,
                scl * 0.05: 17.0 * iscale,
                scl * 0.06: 5.0 * iscale,
                scl * 0.08: 4.0 * iscale,
                scl * 0.2: 0.6 * iscale,
                scl * 2.0: 0.00 * iscale,
                scl * 3.0: 0.0
            })
        ba.animate(
            light, 'radius', {
                0: light_radius * 0.2,
                scl * 0.05: light_radius * 0.55,
                scl * 0.1: light_radius * 0.3,
                scl * 0.3: light_radius * 0.15,
                scl * 1.0: light_radius * 0.05
            })
        ba.timer(scl * 3.0, light.delete)

        # make a scorch that fades over time
        scorch = ba.newnode('scorch',
                            attrs={
                                'position': position,
                                'size': scorch_radius * 0.5,
                                'big': (self.blast_type == 'tnt')
                            })
        if self.blast_type == 'ice':
            scorch.color = (1, 1, 1.5)

        ba.animate(scorch, 'presence', {3.000: 1, 13.000: 0})
        ba.timer(13.0, scorch.delete)

        if self.blast_type == 'ice':
            ba.playsound(factory.hiss_sound, position=light.position)

        lpos = light.position
        ba.playsound(factory.random_explode_sound(), position=lpos)
        ba.playsound(factory.debris_fall_sound, position=lpos)

        ba.camerashake(intensity=5.0 if self.blast_type == 'tnt' else 1.0)

        # tnt is more epic..
        if self.blast_type == 'tnt':
            ba.playsound(factory.random_explode_sound(), position=lpos)

            def _extra_boom() -> None:
                ba.playsound(factory.random_explode_sound(), position=lpos)

            ba.timer(0.25, _extra_boom)

            def _extra_debris_sound() -> None:
                ba.playsound(factory.debris_fall_sound, position=lpos)
                ba.playsound(factory.wood_debris_fall_sound, position=lpos)

            ba.timer(0.4, _extra_debris_sound)
    def handlemessage(self, msg):

        if isinstance(msg, ba.HitMessage):
            if not self.node:
                return None
            if self.node.invincible:
                ba.playsound(SpazFactory.get().block_sound,
                             1.0,
                             position=self.node.position)
                return True

            # If we were recently hit, don't count this as another.
            # (so punch flurries and bomb pileups essentially count as 1 hit)
            local_time = ba.time(timeformat=ba.TimeFormat.MILLISECONDS)
            assert isinstance(local_time, int)
            if (self._last_hit_time is None
                    or local_time - self._last_hit_time > 1000):
                self._num_times_hit += 1
                self._last_hit_time = local_time

            mag = msg.magnitude * self.impact_scale

            velocity_mag = msg.velocity_magnitude * self.impact_scale
            damage_scale = 0.22

            # If they've got a shield, deliver it to that instead.
            if self.shield:
                if msg.flat_damage:
                    damage = msg.flat_damage * self.impact_scale
                else:
                    # Hit our spaz with an impulse but tell it to only return
                    # theoretical damage; not apply the impulse.
                    assert msg.force_direction is not None
                    self.node.handlemessage(
                        'impulse', msg.pos[0], msg.pos[1], msg.pos[2],
                        msg.velocity[0], msg.velocity[1], msg.velocity[2], mag,
                        velocity_mag, msg.radius, 1, msg.force_direction[0],
                        msg.force_direction[1], msg.force_direction[2])
                    damage = damage_scale * self.node.damage

                assert self.shield_hitpoints is not None
                self.shield_hitpoints -= int(damage)
                self.shield.hurt = (
                    1.0 -
                    float(self.shield_hitpoints) / self.shield_hitpoints_max)

                # Its a cleaner event if a hit just kills the shield
                # without damaging the player.
                # However, massive damage events should still be able to
                # damage the player. This hopefully gives us a happy medium.
                max_spillover = SpazFactory.get().max_shield_spillover_damage
                if self.shield_hitpoints <= 0:

                    # FIXME: Transition out perhaps?
                    self.shield.delete()
                    self.shield = None
                    ba.playsound(SpazFactory.get().shield_down_sound,
                                 1.0,
                                 position=self.node.position)

                    # Emit some cool looking sparks when the shield dies.
                    npos = self.node.position
                    ba.emitfx(position=(npos[0], npos[1] + 0.9, npos[2]),
                              velocity=self.node.velocity,
                              count=random.randrange(20, 30),
                              scale=1.0,
                              spread=0.6,
                              chunk_type='spark')

                else:
                    ba.playsound(SpazFactory.get().shield_hit_sound,
                                 0.5,
                                 position=self.node.position)

                # Emit some cool looking sparks on shield hit.
                assert msg.force_direction is not None
                ba.emitfx(position=msg.pos,
                          velocity=(msg.force_direction[0] * 1.0,
                                    msg.force_direction[1] * 1.0,
                                    msg.force_direction[2] * 1.0),
                          count=min(30, 5 + int(damage * 0.005)),
                          scale=0.5,
                          spread=0.3,
                          chunk_type='spark')

                # If they passed our spillover threshold,
                # pass damage along to spaz.
                if self.shield_hitpoints <= -max_spillover:
                    leftover_damage = -max_spillover - self.shield_hitpoints
                    shield_leftover_ratio = leftover_damage / damage

                    # Scale down the magnitudes applied to spaz accordingly.
                    mag *= shield_leftover_ratio
                    velocity_mag *= shield_leftover_ratio
                else:
                    return True  # Good job shield!
            else:
                shield_leftover_ratio = 1.0

            if msg.flat_damage:
                damage = int(msg.flat_damage * self.impact_scale *
                             shield_leftover_ratio)

            else:
                # Hit it with an impulse and get the resulting damage.
                assert msg.force_direction is not None
                if self.multiplyer > 3.0:
                    # at about 8.0 the physics glitch out
                    velocity_mag *= min((3.0 + (self.multiplyer - 3.0) / 4),
                                        7.5)**1.9
                else:
                    velocity_mag *= self.multiplyer**1.9
                self.node.handlemessage(
                    'impulse', msg.pos[0], msg.pos[1], msg.pos[2],
                    msg.velocity[0], msg.velocity[1], msg.velocity[2], mag,
                    velocity_mag, msg.radius, 0, msg.force_direction[0],
                    msg.force_direction[1], msg.force_direction[2])

                damage = int(damage_scale * self.node.damage)
            self.node.handlemessage('hurt_sound')
            # Play punch impact sound based on damage if it was a punch.
            if msg.hit_type == 'punch':
                self.on_punched(damage)

                # If damage was significant, lets show it.
                if damage > 350:
                    assert msg.force_direction is not None
                    ba.show_damage_count('-' + str(int(damage / 10)) + '%',
                                         msg.pos, msg.force_direction)

                # Let's always add in a super-punch sound with boxing
                # gloves just to differentiate them.
                if msg.hit_subtype == 'super_punch':
                    ba.playsound(SpazFactory.get().punch_sound_stronger,
                                 1.0,
                                 position=self.node.position)
                if damage > 500:
                    sounds = SpazFactory.get().punch_sound_strong
                    sound = sounds[random.randrange(len(sounds))]
                else:
                    sound = SpazFactory.get().punch_sound
                ba.playsound(sound, 1.0, position=self.node.position)

                # Throw up some chunks.
                assert msg.force_direction is not None
                ba.emitfx(position=msg.pos,
                          velocity=(msg.force_direction[0] * 0.5,
                                    msg.force_direction[1] * 0.5,
                                    msg.force_direction[2] * 0.5),
                          count=min(10, 1 + int(damage * 0.0025)),
                          scale=0.3,
                          spread=0.03)

                ba.emitfx(position=msg.pos,
                          chunk_type='sweat',
                          velocity=(msg.force_direction[0] * 1.3,
                                    msg.force_direction[1] * 1.3 + 5.0,
                                    msg.force_direction[2] * 1.3),
                          count=min(30, 1 + int(damage * 0.04)),
                          scale=0.9,
                          spread=0.28)

                # Momentary flash.
                hurtiness = damage * 0.003
                punchpos = (msg.pos[0] + msg.force_direction[0] * 0.02,
                            msg.pos[1] + msg.force_direction[1] * 0.02,
                            msg.pos[2] + msg.force_direction[2] * 0.02)
                flash_color = (1.0, 0.8, 0.4)
                light = ba.newnode('light',
                                   attrs={
                                       'position': punchpos,
                                       'radius': 0.12 + hurtiness * 0.12,
                                       'intensity':
                                       0.3 * (1.0 + 1.0 * hurtiness),
                                       'height_attenuated': False,
                                       'color': flash_color
                                   })
                ba.timer(0.06, light.delete)

                flash = ba.newnode('flash',
                                   attrs={
                                       'position': punchpos,
                                       'size': 0.17 + 0.17 * hurtiness,
                                       'color': flash_color
                                   })
                ba.timer(0.06, flash.delete)

            if msg.hit_type == 'impact':
                assert msg.force_direction is not None
                ba.emitfx(position=msg.pos,
                          velocity=(msg.force_direction[0] * 2.0,
                                    msg.force_direction[1] * 2.0,
                                    msg.force_direction[2] * 2.0),
                          count=min(10, 1 + int(damage * 0.01)),
                          scale=0.4,
                          spread=0.1)
            if self.hitpoints > 0:
                self.multiplyer += min(damage / 2000, 0.15)
                self.set_score_text(
                    str(int((self.multiplyer - 1) * 100)) + "%")
                # It's kinda crappy to die from impacts, so lets reduce
                # impact damage by a reasonable amount *if* it'll keep us alive
                if msg.hit_type == 'impact' and damage > self.hitpoints:
                    # Drop damage to whatever puts us at 10 hit points,
                    # or 200 less than it used to be whichever is greater
                    # (so it *can* still kill us if its high enough)
                    newdamage = max(damage - 200, self.hitpoints - 10)
                    damage = newdamage
                self.node.handlemessage('flash')

                # If we're holding something, drop it.
                if damage > 0.0 and self.node.hold_node:
                    self.node.hold_node = None
                # self.hitpoints -= damage
                self.node.hurt = 1.0 - float(
                    self.hitpoints) / self.hitpoints_max

                # If we're cursed, *any* damage blows us up.
                if self._cursed and damage > 0:
                    ba.timer(
                        0.05,
                        ba.WeakCall(self.curse_explode,
                                    msg.get_source_player(ba.Player)))

                # If we're frozen, shatter.. otherwise die if we hit zero
                if self.frozen and (damage > 200 or self.hitpoints <= 0):
                    self.shatter()
                elif self.hitpoints <= 0:
                    self.node.handlemessage(
                        ba.DieMessage(how=ba.DeathType.IMPACT))

            # If we're dead, take a look at the smoothed damage value
            # (which gives us a smoothed average of recent damage) and shatter
            # us if its grown high enough.
            if self.hitpoints <= 0:
                damage_avg = self.node.damage_smoothed * damage_scale
                if damage_avg > 1000:
                    self.shatter()
            return None
        elif isinstance(msg, ba.PowerupMessage):
            if msg.poweruptype == 'health':
                if self.multiplyer > 2:
                    self.multiplyer *= 0.5
                else:
                    self.multiplyer *= 0.75
                self.multiplyer = max(1, self.multiplyer)
                self.set_score_text(
                    str(int((self.multiplyer - 1) * 100)) + "%")

        super().handlemessage(msg)
        return None