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)
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')
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')
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')
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")
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')
def emit() -> None: ba.emitfx(position=position, velocity=velocity, count=30, spread=2.0, scale=0.4, chunk_type='ice', emit_type='stickers')
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')
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)
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')
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])
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?
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)
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')
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')
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)
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 })
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
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')
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)
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')
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
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)
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)
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