def apply_explosion_effects(self, explosion_center, damage_at_center=100, blast_radius=200, pushback_force_at_center=500, pushback_radius=300): # play explosion sound registry.global_controllers.assets_controller.explosion_sound.play() enemies_to_remove = [] for enemy in self.enemies: # get the distance to the explosion distance_to_explosion = enemy.position.distance(explosion_center) # if within pushback radius... if distance_to_explosion <= pushback_radius: # calculate pushback value, the further from the center, the smaller it is pushback_force_val = pushback_force_at_center * ( 1 - (distance_to_explosion / pushback_radius)) # apply the pushback force by resetting enemy velocity enemy.velocity = (enemy.position - explosion_center ).normalize() * pushback_force_val # if within blast radius... if distance_to_explosion <= blast_radius: # calculate damage, the further from the center, the smaller it is damage = damage_at_center * ( 1 - (distance_to_explosion / blast_radius)) # apply damage enemy.hp -= int(damage) # add the blood splatter animation over the enemy self.scene.root.add_child( Node(z_index=900, transition=NodeSpriteTransition( registry.global_controllers.assets_controller. blood_splatter_frames, duration=140), position=enemy.position, rotation=(enemy.position - explosion_center).to_angle() + math.pi, lifetime=140)) if enemy.hp < 0: # IZ DED! # show the death animation (pick random sprite from few animations we have loaded from one png file) self.scene.root.add_child( Node(z_index=1, transition=NodeSpriteTransition(random.choice( registry.global_controllers.assets_controller. enemy_death_frames), duration=450), position=enemy.position, rotation=enemy.rotation, origin_alignment=Alignment.right, lifetime=random.randint(10000, 20000))) # mark enemy for removal: enemies_to_remove.append(enemy) # removed killed enemies for dead_enemy in enemies_to_remove: self.remove_enemy(dead_enemy)
def on_collision_mg_bullet_enemy(self, arbiter, mg_bullet_pair, enemy_pair): print( "Detected a collision between MG bullet object {} hitbox {} and Enemy object {} hitbox {}" .format(mg_bullet_pair.body, mg_bullet_pair.hitbox, enemy_pair.body, enemy_pair.hitbox)) if arbiter.phase == CollisionPhase.begin: enemy = enemy_pair.body enemy.hp -= 10 # add the blood splatter animation to the scene self.scene.root.add_child( Node(z_index=900, transition=NodeSpriteTransition( registry.global_controllers.assets_controller. blood_splatter_frames, duration=140), position=enemy.position, rotation=mg_bullet_pair.body.rotation + math.pi, lifetime=140)) # add a random bloodstain - make smaller ones more likely since it's a small arms hit :) self.scene.root.add_child( Node(z_index=1, sprite=random.choices(registry.global_controllers. assets_controller.bloodstain_imgs, weights=[5, 3, 1, 0.5])[0], position=enemy.position, rotation=mg_bullet_pair.body.rotation + math.pi, lifetime=random.randint(20000, 40000))) if enemy.hp <= 0: # show death animation self.scene.root.add_child( Node(z_index=1, transition=NodeSpriteTransition(random.choice( registry.global_controllers.assets_controller. enemy_death_frames), duration=450), position=enemy.position, rotation=enemy.rotation, origin_alignment=Alignment.right, lifetime=random.randint(10000, 20000))) # remove enemy node from the scene self.scene.enemies_controller.remove_enemy(enemy) else: enemy.stagger() mg_bullet_pair.body.delete() # remove the bullet from the scene return 0
def __init__(self, position, hp=100, *args, **kwargs): # node's properties super().__init__(body_type=BodyNodeType.dynamic, mass=1, z_index=10, position=position, transition=NodeSpriteTransition(registry.global_controllers.assets_controller.enemy_frames, duration=max(200, random.gauss(400, 100)), loops=0), *args, **kwargs) # create a hitbox and add it as a child node to the Enemy self.add_child(HitboxNode( shape=Polygon([Vector(-8, -19), Vector(8, -19), Vector(8, 19), Vector(-8, 19), Vector(-8, -19)]), mask=HitboxMask.enemy, collision_mask=HitboxMask.all, trigger_id=settings.COLLISION_TRIGGER_ENEMY, )) # custom properties self.hp = hp self.stagger_time_left = 0 # 75% enemies will move towards player and 25% will move randomly if random.randint(0, 100) < 75: self.movement_mode = EnemyMovementMode.MoveToPlayer else: self.movement_mode = EnemyMovementMode.MoveToWaypoint self.current_waypoint = None # for those which move to a waypoint, we'll keep its corrdinates here self.randomize_new_waypoint() # and randomize new waypoint self.acceleration_per_second = 300 # how fast will enemy accelerate self.max_velocity = random.randint(75, 125) # we'll make enemy stop accelerating if velocity is above this value
def __init__(self, *, speed_mod, faded=False, **kwargs): assert self.SPRITE_FRAMES assert self.TRIGGER_ID super().__init__( body_type=BodyNodeType.kinematic, position=random.choice(LANE_ENEMY_SLOTS), velocity=Vector(0, random.uniform(300, 300 + speed_mod)), angular_velocity_degrees=random.uniform(-20, 20), **kwargs, ) self._is_destroying = False self.hitbox = self.add_child( HitboxNode( trigger_id=self.TRIGGER_ID, shape=Circle(48.), # color=Color(1., 0., 0., 0.5), z_index=100, )) self.transitions_manager.set( 'animation', NodeSpriteTransition(self.SPRITE_FRAMES, loops=0, duration=len(self.SPRITE_FRAMES) * self.FRAME_DURATION), ) if faded: self.color = Color(0.5, 0.5, 0.5, 1.)
def __init__(self, **kwargs): self.is_moving = False self.is_frozen = False self.current_lane = 2 self.soap_fuel = self.soap_fuel_max = 30000 super().__init__( body_type=BodyNodeType.kinematic, position=LANE_HERO_SLOTS[self.current_lane], scale=Vector.xy(0.7), **kwargs, ) self.hitbox = self.add_child( HitboxNode( trigger_id=CollisionTrigger.soap, shape=Polygon.from_box(Vector(138, 330)), # color=Color(1., 0., 0., 0.5), z_index=100, )) self.transitions_manager.set( 'animation', NodeSpriteTransition(self.SPRITE_FRAMES, loops=0, duration=len(self.SPRITE_FRAMES) * self.FRAME_DURATION), )
def on_collision_grenade_enemy(self, arbiter, grenade_pair, enemy_pair): if arbiter.phase == CollisionPhase.begin: # show explosion animation self.scene.root.add_child( Node(transition=NodeSpriteTransition( registry.global_controllers.assets_controller. explosion_frames, duration=12 * 75), position=grenade_pair.body.position, z_index=1000, lifetime=12 * 75)) # apply explosion effects to enemies (deal damage & push them back) self.scene.enemies_controller.apply_explosion_effects( grenade_pair.body.position) grenade_pair.body.delete() # remove the grenade from the scene return 0
def __init__(self): self.camera.position = Vector(0., 0.) self.python_img = Sprite(PYTHON_IMAGE_PATH) self.transition = NodeTransitionsSequence([ NodePositionTransition( Vector(-100., -100.), 3., advance_method=AttributeTransitionMethod.add, loops=3, easing=Easing.elastic_in_out), NodePositionTransition( Vector(100., 0.), 3., advance_method=AttributeTransitionMethod.add, easing=Easing.back_in_out), NodeTransitionDelay(1.5), NodeTransitionCallback(lambda node: setattr(node, 'sprite', None)), NodeTransitionsParallel([ NodePositionTransition( Vector(-50., 100.), 3., advance_method=AttributeTransitionMethod.add), NodeRotationTransition(1., 5.), NodeScaleTransition(Vector(2., 2.), 3.), NodeColorTransition(Color(0., 1., 0., 0.5), 4.), ], back_and_forth=True), ]) self.custom_transition = NodeCustomTransition( prepare_func=lambda node: { 'positions': [ Vector(random.uniform(-100, 100), random.uniform( -100, 100)) for _ in range(10) ] }, evaluate_func=lambda state, node, t: setattr( node, 'position', state['positions'][min(int(t * 10), 9)], ), duration=10., loops=5, ) spritesheet_frames = split_spritesheet( Sprite(EXPLOSION_IMAGE_PATH), Vector(64, 64), ) self.sprite_transition = NodeSpriteTransition( spritesheet_frames, 1., loops=0, back_and_forth=True, easing=Easing.quartic_out, ) self.space = self.root.add_child(SpaceNode()) self.obj = self.space.add_child( Node( position=Vector(50., 50.), sprite=self.python_img, transition=self.transition, )) self.custom_obj = self.space.add_child( Node( color=Color(1., 0., 0.), sprite=self.python_img, transition=self.custom_transition, )) self.phys_obj = self.space.add_child( BodyNode( color=Color(0., 1., 1.), position=Vector(-50., 50.), sprite=self.python_img, velocity=Vector(0., 30.), transition=BodyNodeVelocityTransition(Vector(0., -30.), 10.), )) self.animated_obj = self.space.add_child( Node( position=Vector(-20., 10.), transition=self.sprite_transition, ))
def recover_from_stagger(self): # start using the standard sprite animation again self.transition=NodeSpriteTransition(registry.global_controllers.assets_controller.enemy_frames, duration=max(200, random.gauss(400, 100)), loops=0) self.stagger_time_left = 0