def draw(self, screen, camera, image_handler): color = self.color_selected if self.selected else self.color camera.draw_text(screen, self.text, self.position + 0.63 * basis(1), 0.75, color=color, chromatic_aberration=self.selected) val_str = str(self.values[self.selection]).replace(', ', 'x').strip('()') camera.draw_text(screen, val_str, self.position, 0.75, color=color, chromatic_aberration=self.selected) if self.selected: if self.cyclic or self.selection > 0: camera.draw_triangle(screen, self.position - 1.5 * basis(0), 0.75, chromatic_aberration=True) if self.cyclic or self.selection < len(self.values) - 1: camera.draw_triangle(screen, self.position + 1.5 * basis(0), 0.75, np.pi, chromatic_aberration=True)
def update_joints(self): w = normalized(self.body.collider.half_width) * self.direction h = normalized(self.body.collider.half_height) foot_offset = 0.3 * ((self.front_foot.relative_position[1]) + (self.back_foot.relative_position[1])) * basis(1) self.body.set_position(self.position - 0.5 * self.crouched * basis(1) + foot_offset) self.shoulder = self.body.position + 0.15 * ( 1 - self.crouched) * h - 0.2 * w self.back_hip = self.body.position - foot_offset + 0.1 * w - 0.45 * h self.front_hip = self.body.position - foot_offset - 0.1 * w - 0.45 * h if self.destroyed: angle_goal = self.body.angle else: angle_goal = np.arctan(self.hand_goal[1] / (self.hand_goal[0] + 1e-6)) angle_goal = max(self.body.angle - 0.25, min(self.body.angle + 0.25, angle_goal)) self.head.angle += 0.1 * (angle_goal - self.head.angle) self.head.set_position(self.body.position - (1 - self.crouched) * 0.35 * (self.head.angle - self.body.angle) * w * self.direction + 0.2 * self.crouched * w + (1 - 0.5 * self.crouched) * h)
def draw(self, screen, camera, image_handler): if not self.image_path: return if self.vertical: h = self.collider.half_height[1] ny = int(2 * h) for j, y in enumerate(np.linspace(-h, h, ny, False)): if j == 0: l = 2 elif j == ny - 1: l = 0 else: l = 1 pos = self.position + self.image_position + y * basis(1) image = image_handler.images[ f'{self.image_path}_vertical_0_{l}'] camera.draw_image(screen, image, pos, 1, 1, 0.0) else: w = self.collider.half_width[0] nx = int(2 * w) for i, x in enumerate(np.linspace(-w, w, nx, False)): if i == 0: k = 0 elif i == nx - 1: k = 2 else: k = 1 pos = self.position + self.image_position + x * basis(0) image = image_handler.images[f'{self.image_path}_{k}_{0}'] camera.draw_image(screen, image, pos, 1, 1, 0.0)
def __init__(self, position, resolution): self.position = np.array(position, dtype=float) self.max_zoom = resolution[1] / 720 * 50.0 self.zoom = self.max_zoom self.half_width = 0.5 * resolution[0] * basis(0) self.half_height = 0.5 * resolution[1] * basis(1) self.shake = np.zeros(2) self.velocity = np.zeros(2)
def __init__(self, position, width, height): super().__init__(position, image_path='wall') self.add_collider(Rectangle([0, 0], width, height, Group.WALLS)) self.size = 1.0 self.vertical = False self.image_position = 0.5 * basis(0) + 0.04 * basis(1) if width == 1: self.vertical = True self.image_position = np.array([0.0, 0.52])
def __init__(self, option_handler): self.option_handler = option_handler self.state = State.MENU self.level = Level() self.players = dict() self.colliders = [] self.time_scale = 1.0 self.camera = Camera([0, 0], self.option_handler.resolution) self.respawn_time = 50.0 self.menu = MainMenu() self.player_menus = [ PlayerMenu((6 * i - 9) * basis(0)) for i in range(4) ] self.options_menu = OptionsMenu() self.options_menu.set_values(self.option_handler) self.network = None self.network_id = -1 self.obj_id = -1 self.controller_id = 0
def update_occupied_squares(self, colliders): self.clear_occupied_squares(colliders) pos = self.position w = axis_half_width(self.half_width, self.half_height, basis(0)) h = axis_half_width(self.half_width, self.half_height, basis(1)) for i in range(int((pos[0] - w) / GRID_SIZE), min(int((pos[0] + w) / GRID_SIZE) + 1, len(colliders))): for j in range( int((pos[1] - h) / GRID_SIZE), min(int((pos[1] + h) / GRID_SIZE) + 1, len(colliders[i]))): self.occupied_squares.append((i, j)) for i, j in self.occupied_squares: colliders[i][j].append(self)
def draw_triangle(self, screen, position, size, angle=0, color=(255, 255, 255), chromatic_aberration=False): a = size * rotate(np.array([0, 0.5]), angle) b = size * rotate(np.array([0, -0.5]), angle) c = size * rotate(np.array([np.sqrt(3) / 2, 0]), angle) if chromatic_aberration: offset = 0.05 * size * basis(0) points = [ self.world_to_screen(-self.zoom / 100 * p + position - offset) for p in [a, b, c] ] pygame.draw.polygon(screen, (255, 0, 0), points) points = [ self.world_to_screen(-self.zoom / 100 * p + position + offset) for p in [a, b, c] ] pygame.draw.polygon(screen, (0, 255, 255), points) points = [ self.world_to_screen(-self.zoom / 100 * p + position) for p in [a, b, c] ] pygame.draw.polygon(screen, color, points)
def destroy(self, colliders): if self.destroyed: return self.hand.gravity_scale = 1.0 self.back_foot.gravity_scale = 1.0 self.front_foot.gravity_scale = 1.0 self.velocity += 0.5 * basis(1) self.bounce = 0.5 self.angular_velocity = -0.125 * np.sign(self.velocity[0]) self.destroyed = True if self.object: self.throw_object(0) self.hand.image_path = 'hand' self.hand.image_position[:] = np.zeros(2) self.timer = 0.0 self.collider.group = Group.DEBRIS if not self.head.destroyed: self.head.collision_enabled = False self.body.collision_enabled = False self.collider.half_height[1] = 0.75
def draw(self, screen, camera, image_handler): self.back_foot.image_path = f'foot_{self.body_type}' self.back_foot.draw(screen, camera, image_handler) self.draw_limb(self.back_hip, self.back_foot.position, self.leg_length, screen, camera, image_handler, 'leg', 'knee', -1) if not self.destroyed and self.back_hand.image_path: self.draw_limb(self.shoulder + 0.25 * self.direction * basis(0), self.back_hand.position, 1.0, screen, camera, image_handler, 'arm', 'elbow') self.body.draw(screen, camera, image_handler) self.front_foot.image_path = f'foot_{self.body_type}' self.front_foot.draw(screen, camera, image_handler) self.draw_limb(self.front_hip, self.front_foot.position, self.leg_length, screen, camera, image_handler, 'leg', 'knee', -1) self.head.draw(screen, camera, image_handler) if self.object and self.object.collider: if self.object.collider.group is Group.SHIELDS: self.draw_limb(self.shoulder, self.hand.position, self.arm_length, screen, camera, image_handler, 'arm', 'elbow') self.object.draw(screen, camera, image_handler) elif self.back_hand.image_path: pos = self.object.get_hand_position() self.draw_limb(self.shoulder, pos, self.arm_length, screen, camera, image_handler, 'arm', 'elbow') if type(self.object) is Bow and self.attack_charge: self.object.arrow.draw(screen, camera, image_handler) self.object.draw(screen, camera, image_handler) self.back_hand.angle = self.object.angle self.back_hand.draw(screen, camera, image_handler) self.hand.draw(screen, camera, image_handler) else: self.object.draw(screen, camera, image_handler) self.draw_limb(self.shoulder, self.hand.position, self.arm_length, screen, camera, image_handler, 'arm', 'elbow') self.hand.draw(screen, camera, image_handler) else: self.draw_limb(self.shoulder, self.hand.position, self.arm_length, screen, camera, image_handler, 'arm', 'elbow') self.hand.draw(screen, camera, image_handler) for b in self.particle_clouds: b.draw(screen, camera, image_handler)
def overlap(self, other): overlap = np.zeros(2) if type(other) is Circle: dist = norm2(self.position - other.position) if dist > (self.radius + other.radius)**2: return overlap if dist == 0.0: overlap = (self.radius + other.radius) * basis(1) else: dist = np.sqrt(dist) unit = (self.position - other.position) / dist overlap = (self.radius + other.radius - dist) * unit elif type(other) is Rectangle: overlap = -other.overlap(self) return overlap
def __init__(self, position): super().__init__(position, image_path='', size=0.8) self.image_position = 0.15 * basis(0) self.add_collider(Circle([0, 0], 0.1, Group.DEBRIS)) self.gravity_scale = 0.0 self.add_animation(0.3 * np.ones(1), np.zeros(1), np.zeros(1), 'idle') xs = 0.3 * np.ones(4) ys = np.array([0, 0.25, 0.3, 0.32]) angles = np.array([0, -0.25, -0.25, -0.25]) self.add_animation(xs, ys, angles, 'jump') xs = 0.25 * np.array([3, 2, 1, 0, 0.5, 1, 2, 2.5, 3]) ys = 0.25 * np.array([0, 0, 0, 0, 0.5, 1, 1, 0.5, 0]) angles = 0.25 * np.array([0, 0, 0, 0, -1, -1, -1, 0, 0]) self.add_animation(xs, ys, angles, 'walk') self.loop_animation('idle')
def draw(self, screen, image_handler): if self.state in [State.PLAY, State.LAN]: screen.fill((0, 0, 0)) screen.blit( self.level.background, self.camera.world_to_screen(np.array([0, self.level.height]))) if self.option_handler.shadows: self.level.draw_shadow(screen, self.camera, image_handler) for p in self.players.values(): p.draw_shadow(screen, self.camera, image_handler, self.level.light) self.level.draw(screen, self.camera, image_handler) for player in self.players.values(): player.draw(screen, self.camera, image_handler) if self.option_handler.debug_draw: self.debug_draw(screen, image_handler) elif self.state is State.MENU: screen.fill((50, 50, 50)) self.menu.draw(screen, self.camera, image_handler) elif self.state is State.PLAYER_SELECT: screen.fill((50, 50, 50)) for pm in self.player_menus: pm.draw(screen, self.camera, image_handler) if pm.controller_id is not None: self.players[pm.controller_id].set_position(pm.position + 3 * basis(1)) self.players[pm.controller_id].on_ground = True self.players[pm.controller_id].animate(0.0) self.players[pm.controller_id].draw( screen, self.camera, image_handler) elif self.state is State.OPTIONS: screen.fill((50, 50, 50)) self.options_menu.draw(screen, self.camera, image_handler)
def damage(self, amount, colliders): self.parent.damage(amount, colliders) self.blood.append(((np.random.random() - 0.5) * basis(1), 2 * np.pi * np.random.random())) return BloodSplatter
def update(self, gravity, time_step, colliders): if self.health <= 0: self.destroy(colliders) if norm(self.camera_shake) < time_step: self.camera_shake = np.zeros(2) else: self.camera_shake *= -0.5 for b in self.particle_clouds: b.update(gravity, time_step) if not b.active: self.particle_clouds.remove(b) if self.destroyed: self.update_ragdoll(gravity, time_step, colliders) self.update_joints() return self.grab_timer = max(0, self.grab_timer - time_step) if self.velocity[1] != 0: self.on_ground = False delta_pos = self.velocity * time_step # + 0.5 * self.acceleration * time_step**2 self.position += delta_pos self.collider.position += delta_pos self.collider.update_occupied_squares(colliders) self.head.collider.update_occupied_squares(colliders) self.body.collider.update_occupied_squares(colliders) self.collider.update_collisions(colliders) if not self.collision_enabled: return for collision in self.collider.collisions: obj = collision.collider.parent if not obj.collision_enabled: continue if obj.collider.group is Group.PLATFORMS: if self.position[1] - self.collider.half_height[1] - delta_pos[1] \ < obj.position[1] + obj.collider.half_height[1] + 0.5 * gravity[1] * time_step**2: continue if collision.overlap[1] > 0: if self.velocity[1] < -0.1: self.goal_crouched = -0.5 * self.velocity[1] / time_step self.sounds.add('bump') self.sounds.add('walk') self.on_ground = True self.velocity[1] = 0.0 elif collision.overlap[1] < 0: if self.velocity[1] > 0.1: self.sounds.add('bump') self.velocity[1] *= -1 self.position += collision.overlap self.collider.position += collision.overlap if not collision.overlap[1]: self.velocity[0] = 0.0 self.acceleration[:] = gravity if np.any(self.goal_velocity[0]): self.acceleration[0] = (self.goal_velocity[0] - self.velocity[0]) * self.walk_acceleration else: self.acceleration[0] = (self.goal_velocity[0] - self.velocity[0] ) * 2 * self.walk_acceleration self.velocity += self.acceleration * time_step self.speed = norm(self.velocity) if self.speed > 0: self.velocity *= min(self.speed, MAX_SPEED) / self.speed self.crouched += (self.goal_crouched - self.crouched) * self.crouch_speed * time_step self.body.rotate(-0.5 * self.velocity[0] - 0.5 * self.direction * self.crouched - self.body.angle) self.update_joints() self.collider.position[1] = self.position[1] - 0.5 * self.crouched self.collider.half_height[1] = 1.5 - 0.5 * self.crouched d = self.hand_goal[0] if abs(d) > 0.1 and np.sign(d) != self.direction: self.flip_horizontally() r = self.hand.position - self.shoulder if abs(r[0]) > 0.1: self.hand.angular_velocity = 0.5 * (np.arctan(r[1] / r[0]) - self.hand.angle) else: self.hand.angular_velocity = 0.0 self.animate(time_step) # purkka if self.back_hand.image_path: self.back_hand.set_position(self.object.get_grip_position()) if self.object: self.object.set_position(self.object.position + time_step * self.velocity) if self.object.collider: self.object.collider.update_occupied_squares(colliders) hand_pos = self.shoulder + ( 1 - 0.5 * self.throw_charge) * self.hand_goal self.object.velocity = 0.5 * ( hand_pos - self.object.position) - 0.125 * gravity * basis(1) if not self.object.collider: self.throw_object() return if self.object.collider.group in { Group.GUNS, Group.SHIELDS, Group.SWORDS }: if abs(d) > 0.1 and np.sign(d) != self.object.direction: self.object.flip_horizontally() self.hand.set_position(hand_pos) self.hand.velocity[:] = np.zeros(2) if type(self.object) not in {Bow, Shield, Grenade } and self.object.timer > 0: if self.object.hit: self.hand.play_animation('idle') self.hand.animate(time_step) self.object.set_position(self.hand.position) # experimental PhysicsObject.update(self.object, gravity, time_step, colliders) self.hand.set_position(self.object.position) self.hand.velocity[:] = np.zeros(2) self.object.rotate(self.hand.angle - self.object.angle) if norm(self.shoulder - self.object.position) > 1.6 * self.arm_length: if isinstance(self.object, Weapon): self.object.set_position(self.position) self.object.collider.update_occupied_squares(colliders) self.hand.set_position(self.object.position) self.hand.velocity[:] = np.zeros(2) else: self.throw_object(0.0) else: self.hand.set_position(self.object.position) self.hand.velocity[:] = np.zeros(2) self.hand.update(gravity, time_step, colliders) else: self.hand.set_position(self.hand.position + time_step * self.velocity) self.hand.velocity = self.shoulder + self.hand_goal - self.hand.position - 0.185 * gravity * basis( 1) self.hand.update(gravity, time_step, colliders) self.hand.collider.update_occupied_squares(colliders) self.hand.collider.update_collisions( colliders, {Group.PROPS, Group.GUNS, Group.SHIELDS, Group.SWORDS})
def __init__(self, position=(0, 0), controller_id=0, network_id=0): super().__init__(position) self.goal_crouched = 0.0 self.goal_velocity = np.zeros(2) self.walk_acceleration = 0.25 self.bounce = 0.0 self.add_collider(Rectangle([0, 0], 0.8, 3, Group.PLAYERS)) self.body_type = 'speedo' self.head_type = 'bald' self.body = Body(self.position, self) self.head = Head(self.position + basis(1), self) self.back_hip = self.position + np.array([0.1, -0.5]) self.front_hip = self.position + np.array([-0.1, -0.5]) self.back_foot = Foot(self.position - np.array([0.35, 1.4])) self.front_foot = Foot(self.position - np.array([0.55, 1.4])) self.leg_length = 0.95 self.max_speed = 0.5 self.shoulder = self.position + 0.25 * 2 / 3 * self.collider.half_height \ - 0.1 * self.direction * self.collider.half_width self.hand_goal = basis(0) self.arm_length = 1.0 self.elbow = np.zeros(2) self.hand = Hand(self.position) self.back_hand = AnimatedObject(self.position, '', 1.2) self.object = None self.crouched = 0 self.crouch_speed = 1.0 self.lt_pressed = False self.rt_pressed = False self.attack_charge = 0.0 self.throw_speed = 1.0 self.throw_charge = 0.0 self.charge_speed = 0.05 self.controller_id = controller_id self.network_id = network_id self.timer = 0.0 self.walking = False #self.channel = pygame.mixer.Channel(self.controller_id + 1) self.camera_shake = np.zeros(2) self.rest_angle = 0.5 * np.pi self.grab_delay = 1.0 self.grab_timer = 0.0 self.jump_speed = 0.65
def __init__(self, position, radius, group=Group.NONE): super().__init__(position, group) self.radius = radius self.half_height = radius * basis(1) self.half_width = radius * basis(0)
def set_resolution(self, resolution): self.max_zoom = resolution[1] / 720 * 50.0 self.zoom = self.max_zoom self.half_width = 0.5 * resolution[0] * basis(0) self.half_height = 0.5 * resolution[1] * basis(1)
def update_buttons(self): for i, b in enumerate(self.buttons): b.set_position(self.position - 1.5 * i * basis(1))
def update(self, gravity, time_step, colliders): for p in self.particle_clouds: p.update(gravity, time_step) if not p.active: self.particle_clouds.remove(p) if not self.active: return if self.velocity[1] != 0: self.on_ground = False self.speed = norm(self.velocity) if self.speed != 0: self.velocity *= min(self.speed, MAX_SPEED) / self.speed delta_pos = self.velocity * time_step + 0.5 * self.acceleration * time_step**2 self.set_position(self.position + delta_pos) acc_old = self.acceleration.copy() self.acceleration = self.get_acceleration(gravity) if self.rest_angle is None: self.angular_velocity = -self.gravity_scale * self.velocity[0] delta_angle = self.angular_velocity * time_step + 0.5 * self.angular_acceleration * time_step**2 if delta_angle: self.rotate(delta_angle) ang_acc_old = float(self.angular_acceleration) self.angular_acceleration = 0.0 if self.collider is None or not self.collision_enabled: return if any(np.abs(delta_pos) > 0.01) or abs(delta_angle) > 1e-3: self.collider.update_occupied_squares(colliders) self.collider.update_collisions(colliders) for collision in self.collider.collisions: collider = collision.collider if not collider.parent.collision_enabled: continue if collider.group is Group.PLATFORMS: if self.parent and self.collider.group in {Group.GUNS, Group.SWORDS, Group.SHIELDS}: self.collider.collisions.remove(collision) continue if collider.half_height[1] > 0: if self.collider.position[1] - delta_pos[1] - self.collider.axis_half_width(basis(1)) \ < collider.position[1] + collider.half_height[1]: self.collider.collisions.remove(collision) continue elif self.collider.position[1] - delta_pos[1] + self.collider.axis_half_width(basis(1)) \ > collider.position[1] + collider.half_height[1]: self.collider.collisions.remove(collision) continue if self.collider.group is Group.THROWN: if collider.parent is self.parent: self.collider.collisions.remove(collision) continue try: collider.parent.parent.throw_object() except AttributeError: pass if collision.overlap[1] > 0: self.on_ground = True if not self.parent: if self.rest_angle is not None: self.rotate(-self.angle + self.direction * self.rest_angle) self.angular_velocity = 0.0 elif collision.overlap[0] != 0: self.angular_velocity *= -1 n = min(int(self.speed * 5), 10) if n > 1: self.particle_clouds.append(Dust(self.position, self.speed * normalized(collision.overlap), n)) self.set_position(self.position + collision.overlap) n = collision.overlap self.velocity -= 2 * self.velocity.dot(n) * n / norm2(n) self.velocity *= self.bounce if not self.parent and isinstance(collider.parent, PhysicsObject): collider.parent.velocity[:] = -self.velocity if self.collider.group is Group.THROWN and self.collider.collisions: self.parent = None self.collider.group = self.group self.velocity += 0.5 * (acc_old + self.acceleration) * time_step self.angular_velocity += 0.5 * (ang_acc_old + self.angular_acceleration) * time_step if abs(self.velocity[0]) < 0.05: self.velocity[0] = 0.0 if self.parent is None and self.collider.collisions and self.speed > 0.1: self.sounds.add(self.bump_sound)