Exemplo n.º 1
0
    def shoot_shotgun(self, bodies):
        shots = list()
        for i in range(7):
            shotpos = copy.copy(self.pos)
            shotvel = 300 + (2 * random.random() - 1) * 100
            shotdir = copy.copy(
                self.rot[0]) + (2 * random.random() - 1) * c.PI / 12
            shotpos[0] += Vector(math.cos(self.rot[0]), math.sin(
                self.rot[0])) * self.radius[0]
            shotpos[1] += Vector(math.cos(shotdir),
                                 math.sin(shotdir)) * shotvel
            shot = Shot(ipos=shotpos,
                        imass=[0.001],
                        iradius=[3],
                        friction=1,
                        elasticity=0.2,
                        colour=self.colour,
                        damage=20,
                        health=20,
                        duration=100)
            bodies.append(shot)
            self.pos[1] -= Vector(
                math.cos(shotdir),
                math.sin(shotdir)) * shotvel * shot.mass[0] / self.mass[0]
            self.colliding_with.append(shot)
            shot.colliding_with.append(self)
            shots.append(shot)

        for shot in shots:
            others = copy.copy(shots)
            others.remove(shot)
            shot.colliding_with += others

        self.shot_cooldown = self.ishot_cooldown
    def get_col_acc(self, other_bodies):
        a = Vector(0, 0)
        for other in other_bodies:
            if self.test_collision(other):
                selfpara, selfperp = self.pos[1].resolve_about(self.pos[0] -
                                                               other.pos[0])
                otherpara, otherperp = other.pos[1].resolve_about(self.pos[0] -
                                                                  other.pos[0])
                m1, m2 = self.mass[0], other.mass[0]
                selfparanew = (m1 - m2) / (m1 + m2) * selfpara + 2 * m2 / (
                    m1 + m2) * otherpara
                vf = selfparanew * self.elasticity + selfperp

                a += (vf - self.pos[1]) / c.DT

                if self.health is not None and other.damage is not None:
                    if isinstance(self, Player_class.Player) and isinstance(
                            other, Player_class.Player):
                        damage = round(other.damage *
                                       (self.pos[1] - other.pos[1]).norm /
                                       1200)
                        if other.pos[1].norm > self.pos[1].norm * 4:
                            damage *= 2
                    else:
                        damage = other.damage
                    self.health -= damage
                    if isinstance(self, Player_class.Player):
                        Body.damage_markers.append([
                            damage, self.pos[0] - Vector(20, 20),
                            other.colour[0],
                            max(min(damage, 100), 30)
                        ])

        return a
 def __init__(self,
              ipos=[Vector(), Vector()],
              imass=[0],
              icharge=[0],
              iradius=[2],
              iseparation=[0],
              ialignment=[0],
              icohesion=[0],
              iview_radius=[0],
              friction=0,
              elasticity=1,
              colour=((255, 255, 255), (170, 170, 170), (85, 85, 85)),
              health=None,
              damage=None):
     """self.pos is a list of all derivatives of an object's position up to the constant term etc"""
     self.prev_pos, self.pos, self.next_pos = None, ipos, None  # (x, dx,...)
     self.prev_mass, self.mass, self.next_mass = None, imass, None  # (m, dm,...)
     self.prev_charge, self.charge, self.next_charge = None, icharge, None  # (q, dq,...)
     self.prev_radius, self.radius, self.next_radius = None, iradius, None  # (r, dr,...)
     self.prev_cohesion, self.cohesion, self.next_cohesion = None, icohesion, None  # (coh, dcoh,...)
     self.prev_alignment, self.alignment, self.next_alignment = None, ialignment, None  # (ali, dali,...)
     self.prev_separation, self.separation, self.next_separation = None, iseparation, None  # (sep, dsep,...)
     self.prev_view_radius, self.view_radius, self.next_view_radius = None, iview_radius, None  # (rad, drad,...)
     self.friction = friction  # linear friction
     self.elasticity = elasticity  # collision elasticity
     self.colour = colour  # (light, middle, dark)
     self.health = health
     self.damage = damage
     """nonzero masses have extra collision acceleration from any masses they are touching"""
     self.colliding_with = list(
     )  # list of objects currently being collided with
Exemplo n.º 4
0
    def get_ctrl_acc(self, other_bodies):
        a = Vector(0, 0)

        if self.controls is not None:
            if self.thrust == 1:
                a += self.controls[0][1] * Vector(math.cos(self.rot[0]),
                                                  math.sin(self.rot[0]))

            if self.thrust == -1:
                a += self.controls[2][1] * Vector(math.cos(self.rot[0]),
                                                  math.sin(self.rot[0]))

        return a
 def draw(self, screen, fixed_to_screen=False, colour=None):
     if self.pos is not None:
         if colour is None:
             colour = self.colour
         if fixed_to_screen:
             P = self.pos[0]
         else:
             P = self.pos[0] - Body.cmp
         pygame.draw.circle(screen, colour[0],
                            round(P + Vector(*screen.get_size()) / 2),
                            round(self.radius[0]))
         if self.radius[0] >= 3:
             pygame.draw.circle(screen, colour[1],
                                round(P + Vector(*screen.get_size()) / 2),
                                round(self.radius[0] - 2))
 def get_sep_acc(self, other_bodies):
     a = Vector(0, 0)
     for other in other_bodies:
         distance = self.pos[0].distance_to(other.pos[0])
         if distance > 0 and distance <= self.view_radius[0]:
             direction = (other.pos[0] - self.pos[0]).normalize()
             radii = self.radius[0] + other.radius[0]
             a += direction * c.SEP * self.separation[0] * other.separation[
                 0] / distance**2
     return a
 def get_grav_acc(self, other_bodies):
     a = Vector(0, 0)
     for other in other_bodies:
         distance = self.pos[0].distance_to(other.pos[0])
         if distance > 0:
             direction = (other.pos[0] - self.pos[0]).normalize()
             radii = self.radius[0] + other.radius[0]
             a += direction * c.GRAV * other.mass[0] * (
                 distance**2 + radii**4 * distance**-2)**-1
     return a
 def get_coh_acc(self, other_bodies):
     a = Vector(0, 0)
     sum_position = sum_cohesion = 0
     for other in other_bodies:
         distance = self.pos[0].distance_to(other.pos[0])
         if distance > 0 and distance <= self.view_radius[0]:
             sum_position += other.cohesion[0] * other.pos[0]
             sum_cohesion += other.cohesion[0]
     if sum_cohesion > 0:
         a = c.COH * sum_position / sum_cohesion - self.pos[0]
     return a
    def set_next(self, screen, dt, other_bodies):
        """
        calculates a body's attributes after a frame using its derivatives
        """
        self.next_pos = list()
        for i in range(max(len(self.pos) - 1, 2)):
            if len(self.pos) > 2 or i == 0:
                dxi = self.pos[i + 1] * dt
            else:
                dxi = Vector(0, 0)
            if i == 1:  # change in velocity
                for acc_func in self.acc_funcs:
                    # self.draw_vector(screen, acc_func(self, other_bodies))
                    a = acc_func(self, other_bodies)
                    dxi += a * dt
            self.next_pos.append(self.pos[i] + dxi)
        if len(self.pos) > 2:
            self.next_pos.append(self.pos[-1])

        self.next_mass = list()
        for i in range(len(self.mass) - 1):
            dxi = self.mass[i + 1] * dt
            self.next_mass.append(self.mass[i] + dxi)
        self.next_mass.append(self.mass[-1])

        self.next_charge = list()
        for i in range(len(self.charge) - 1):
            dxi = self.charge[i + 1] * dt
            self.next_charge.append(self.charge[i] + dxi)
        self.next_charge.append(self.charge[-1])

        self.next_radius = list()
        for i in range(len(self.radius) - 1):
            dxi = self.radius[i + 1] * dt
            self.next_radius.append(self.radius[i] + dxi)
        self.next_radius.append(self.radius[-1])

        self.next_cohesion = list()
        for i in range(len(self.cohesion) - 1):
            dxi = self.cohesion[i + 1] * dt
            self.next_cohesion.append(self.cohesion[i] + dxi)
        self.next_cohesion.append(self.cohesion[-1])

        self.next_alignment = list()
        for i in range(len(self.alignment) - 1):
            dxi = self.alignment[i + 1] * dt
            self.next_alignment.append(self.alignment[i] + dxi)
        self.next_alignment.append(self.alignment[-1])

        self.next_separation = list()
        for i in range(len(self.separation) - 1):
            dxi = self.separation[i + 1] * dt
            self.next_separation.append(self.separation[i] + dxi)
        self.next_separation.append(self.separation[-1])
Exemplo n.º 10
0
 def get_ali_acc(self, other_bodies):
     a = Vector(0, 0)
     sum_velocity = sum_alignment = 0
     for other in other_bodies:
         distance = self.pos[0].distance_to(other.pos[0])
         if distance > 0 and distance <= self.view_radius[0]:
             sum_velocity += other.alignment[0] * other.pos[1]
             sum_alignment += other.alignment[0]
     if sum_alignment > 0:
         a = c.ALI * sum_velocity / sum_alignment - self.pos[1]
     return a
Exemplo n.º 11
0
 def get_stat_acc(self, other_bodies):
     a = Vector(0, 0)
     if self.mass[0] != 0:
         for other in other_bodies:
             distance = self.pos[0].distance_to(other.pos[0])
             if distance > 0:
                 direction = (other.pos[0] - self.pos[0]).normalize()
                 radii = self.radius[0] + other.radius[0]
                 a += direction * c.STAT * self.charge[0] * other.charge[
                     0] / (distance**2 +
                           radii**4 * distance**-2) / self.mass[0]
     return a
Exemplo n.º 12
0
 def update_cmp(cls, bodies, fast=False):
     cmp = Vector(0, 0)
     if len(bodies) != 0:
         cmm = 0
         for body in bodies:
             cmp += body.mass[0] * body.pos[0]
             cmm += body.mass[0]
         if cmm > 0:
             cmp /= cmm
     if fast:
         Body.cmp = cmp
     else:
         Body.cmp = (7 * Body.cmp + cmp) / 8
Exemplo n.º 13
0
 def get_mag_acc(self, other_bodies):
     a = Vector(0, 0)
     if self.mass[0] != 0:
         for other in other_bodies:
             distance = self.pos[0].distance_to(other.pos[0])
             if distance > 0:
                 radius = (self.pos[0] - other.pos[0]).normalize()
                 direction = self.pos[1].rotate(c.PI / 2)
                 radii = self.radius[0] + other.radius[0]
                 axb = other.pos[1].cross(radius)
                 a += direction * c.MAG * self.charge[0] * other.charge[
                     0] * axb / (distance**2 +
                                 radii**4 * distance**-2) / self.mass[0]
     return a
Exemplo n.º 14
0
 def shoot_gun(self, bodies):
     shotpos = copy.copy(self.pos)
     shotvel = 225
     shotdir = self.rot[0]
     shotpos[0] += Vector(math.cos(shotdir),
                          math.sin(shotdir)) * self.radius[0]
     shotpos[1] += Vector(math.cos(shotdir), math.sin(shotdir)) * shotvel
     shot = Shot(ipos=shotpos,
                 imass=[0.1],
                 iradius=[5],
                 friction=1 / 2000,
                 elasticity=0.2,
                 colour=self.colour,
                 damage=40,
                 health=60,
                 duration=300)
     bodies.append(shot)
     self.pos[1] -= Vector(
         math.cos(shotdir),
         math.sin(shotdir)) * shotvel * shot.mass[0] / self.mass[0]
     self.colliding_with.append(shot)
     shot.colliding_with.append(self)
     self.shot_cooldown = self.ishot_cooldown
Exemplo n.º 15
0
def delta_angle_test():
    pid = PID()
    wanted = Vector(0.0, 0.0)
    current = Vector(0.0, 0.0)
    piv = Vector()

    pid.set_wanted(wanted)

    for i in range(0, 360, 1):
        wanted.set(0.0, i)
        pid.set_wanted(wanted)

        for j in range(0, 360, 1):
            current.set(0.0, j)
            piv = pid._get_delta_angle(current)
            if j in (45, 90, 135, 180, 225, 270, 315,
                     360) and i in (45, 90, 135, 180, 225, 270, 315, 360):
                print("current angle: ", j, " | wanted angle: ", i)
                print("closest angle: ", round(piv, 0))
                print("")
Exemplo n.º 16
0
    def draw_bodies(cls, screen, bodies, fixed_to_screen=False, colour=None):
        for body in bodies:
            body.draw(screen, fixed_to_screen, colour)

        if not fixed_to_screen:
            for damage_marker in cls.damage_markers:
                damage_marker[3] -= 1
                if damage_marker[3] >= 0:
                    surface = pygame.font.SysFont("Consolas", 15).render(
                        str(damage_marker[0]), True, damage_marker[2])
                    screen.blit(
                        surface,
                        round(damage_marker[1] - Body.cmp +
                              Vector(*screen.get_size()) / 2))
                else:
                    cls.damage_markers.remove(damage_marker)
Exemplo n.º 17
0
    def __init__(self,
                 ipos=[Vector()],
                 imass=[0],
                 icharge=[0],
                 iradius=[2],
                 iseparation=[0],
                 ialignment=[0],
                 icohesion=[0],
                 iview_radius=[0],
                 friction=0,
                 elasticity=1,
                 irot=[0, 0],
                 colour=((255, 255, 255), (170, 170, 170), (85, 85, 85)),
                 rotfriction=0,
                 controls=None,
                 shot_type=0,
                 health=None,
                 damage=None,
                 stocks=None):

        self.ipos = ipos
        self.imass = imass
        self.icharge = icharge
        self.iradius = iradius
        self.icohesion = icohesion
        self.ialignment = ialignment
        self.iseparation = iseparation
        self.iview_radius = iview_radius
        self.irot = irot
        self.ihealth = health
        self.istocks = stocks
        self.ishot_cooldown = (40, 120)[shot_type]
        """initiates a player body with a rotation and controls"""
        super().__init__(ipos, imass, icharge, iradius, iseparation,
                         ialignment, icohesion, iview_radius, friction,
                         elasticity, colour, health, damage)

        self.thrust = 0
        self.turning = 0
        self.prev_rot, self.rot, self.next_rot = None, irot, None
        self.rotfriction = rotfriction

        self.controls = controls
        self.shot_cooldown = (40, 120)[shot_type]
        self.shot_type = shot_type
        self.stocks = stocks
Exemplo n.º 18
0
    def draw(self, screen, fixed_to_screen, colour=None):
        if colour is None:
            colour = self.colour
        Body.draw(self, screen, fixed_to_screen, colour)
        if self.radius[0] >= 3:
            P = self.pos[0] + self.radius[0] * Vector(math.cos(self.rot[0]),
                                                      math.sin(self.rot[0]))
            if not fixed_to_screen:
                P -= Body.cmp
            pygame.draw.circle(screen, colour[1],
                               round(P + Vector(*screen.get_size()) / 2), 3)

        if self.stocks is not None:
            for i in range(self.stocks + 1):
                pygame.draw.circle(
                    screen, colour[0],
                    round(self.ipos[0] + Vector(*screen.get_size()) / 2 +
                          i * Vector(20, 0) + Vector(-30, -15)), 6)
        if self.health > 0:
            P1 = round(self.ipos[0] + Vector(*screen.get_size()) / 2 +
                       Vector(-50, 0))
            P2 = round(self.ipos[0] + Vector(*screen.get_size()) / 2 +
                       Vector(max(self.health, 0) - 50, 0))
            pygame.draw.line(screen, colour[1], P1, P2, 2)
Exemplo n.º 19
0
    def __init__(self,
                 ipos=[Vector()],
                 imass=[0],
                 icharge=[0],
                 iradius=[2],
                 iseparation=[0],
                 ialignment=[0],
                 icohesion=[0],
                 iview_radius=[0],
                 friction=0,
                 elasticity=1,
                 colour=((255, 255, 255), (170, 170, 170), (85, 85, 85)),
                 health=None,
                 damage=None,
                 duration=None):
        super().__init__(ipos, imass, icharge, iradius, iseparation,
                         ialignment, icohesion, iview_radius, friction,
                         elasticity, colour, health, damage)

        self.duration = duration
Exemplo n.º 20
0
 def _set_derivator(self, derivator = Vector(0.0,0.0)):
     self.Derivator = derivator
Exemplo n.º 21
0
 def _set_integrator(self, integrator = Vector(0.0,0.0)):
     self.Integrator = integrator
Exemplo n.º 22
0
    def __init__(self): 
        '''Initialises values for future PID calculations'''
        self.Kp=Vector(0.85,1.0) #mindre
        self.Ki=Vector(0.0,0.0) #set for test
        self.Kd=Vector(0.1,0.0) #set for test

        self.Derivator=Vector()
        self.Integrator=Vector()
        self.Integrator_max=Vector(50.0,45.0)
        self.Integrator_min=Vector(-50.0,-45.0) 
        self.pid_max = Vector(5.0, 45.0)  
        self.wanted_vector=Vector()
        self.error=Vector()
        self.delta_angle = 0.0
Exemplo n.º 23
0
class PID :
    '''Class to serve ass a PID regulator between wanted and current vector'''
    def __init__(self): 
        '''Initialises values for future PID calculations'''
        self.Kp=Vector(0.85,1.0) #mindre
        self.Ki=Vector(0.0,0.0) #set for test
        self.Kd=Vector(0.1,0.0) #set for test

        self.Derivator=Vector()
        self.Integrator=Vector()
        self.Integrator_max=Vector(50.0,45.0)
        self.Integrator_min=Vector(-50.0,-45.0) 
        self.pid_max = Vector(5.0, 45.0)  
        self.wanted_vector=Vector()
        self.error=Vector()
        self.delta_angle = 0.0

    def update(self, current_vector): 
        '''Calculates new PID value based on current vector input

        Args:
            current_vector: Vector containing current movement and bearing
        
        Returns:
            Vector containing new speed and angle to boat calculated with PID
        '''
        self.error.magnitude = self.wanted_vector.magnitude - current_vector.magnitude 
        self.error.angle = self._get_delta_angle(current_vector)

        self.P_value =  self.error * self.Kp

        self.D_value =  ( self.error - self.Derivator) * self.Kd 
        self.Derivator = self.error

        self.Integrator = self.Integrator + self.error #look at what value we get

        if self.Integrator.magnitude > self.Integrator_max.magnitude or self.Integrator.angle > self.Integrator_max.angle:
            self.Integrator = self.Integrator_max
        elif self.Integrator.magnitude < self.Integrator_min.magnitude or self.Integrator.angle < self.Integrator_min.angle:
            self.Integrator = self.Integrator_min

        
        self.I_value = self.Integrator * self.Ki

        pid = self.P_value + self.I_value + self.D_value

        #linearity fix
        pid.magnitude = current_vector.magnitude + pid.magnitude

        #max speed fix
        if pid.magnitude > self.pid_max.magnitude: 
            pid.magnitude = self.pid_max.magnitude

        elif pid.magnitude < -1.0*self.pid_max.magnitude:
            pid.magnitude = -1.0*self.pid_max.magnitude
            
        if pid.angle > self.pid_max.angle: 
            pid.angle = self.pid_max.angle
            
        elif pid.angle < -1.0*self.pid_max.angle:
            pid.angle = -1.0*self.pid_max.angle

        return pid

    def set_wanted(self, wanted): 
        '''Function for setting wanted vector for PID object

        Args:
            wanted: Vector containing wanted movement in speed and angle relative to North
        '''
        if isinstance(wanted, Vector) or isinstance(wanted, tuple):
            self.wanted_vector = wanted
            self.Integrator.set(0.0,0.0) #For resetting
        if isinstance(wanted, list) or isinstance(wanted, float):
            self.wanted_vector.set(wanted[0], wanted[1])

    def _set_integrator(self, integrator = Vector(0.0,0.0)):
        self.Integrator = integrator

    def _set_derivator(self, derivator = Vector(0.0,0.0)):
        self.Derivator = derivator

    def _get_delta_angle(self, current_vector):
        deltaXY = m.radians(self.wanted_vector.angle - current_vector.angle )
        self.delta_angle = m.degrees(m.atan2(m.sin(deltaXY), m.cos(deltaXY)))
        return self.delta_angle
Exemplo n.º 24
0
    if len(active_players) >= 1:
        for body in bodies:
            body.update(1 / FPS, bodies, G, Q, keys)
        for body in bodies:
            body.step(bodies, screen_dims, frame, damage_markers)

    screen.fill((0, 0, 0))

    if not in_play:
        for i, player in enumerate(players):
            if player not in bodies and player not in active_players:
                player.draw(screen, bodies, players)
                screen.blit(
                    FONT.render(player_names[players.index(player)], True,
                                player.colour),
                    player.defP + Vector(-37, -50) +
                    (-1, 1)[players.index(player) in (1, 2)] * Vector(50, 0))
                screen.blit(
                    FONT.render(player.shot_type, True,
                                player.colour), player.defP + Vector(-37, 18) +
                    (-1, 1)[players.index(player) in (1, 2)] * Vector(50, 0))
                screen.blit(
                    FONT.render("{} wins".format(wins[i]), True,
                                player.colour), player.defP + Vector(-37, 40) +
                    (-1, 1)[players.index(player) in (1, 2)] * Vector(50, 0))
        if winner is not None:
            surface = FONT.render(
                "{} wins as {}!".format(player_names[winner[0]], winner[2]),
                True, winner[1])
            rect = (int(screen_dims[0] / 2 - surface.get_rect().w / 2),
                    int(screen_dims[1] / 2 - surface.get_rect().h / 2))
Exemplo n.º 25
0
class Body(object):
    cmp = Vector(0, 0)
    damage_markers = list()

    def __init__(self,
                 ipos=[Vector(), Vector()],
                 imass=[0],
                 icharge=[0],
                 iradius=[2],
                 iseparation=[0],
                 ialignment=[0],
                 icohesion=[0],
                 iview_radius=[0],
                 friction=0,
                 elasticity=1,
                 colour=((255, 255, 255), (170, 170, 170), (85, 85, 85)),
                 health=None,
                 damage=None):
        """self.pos is a list of all derivatives of an object's position up to the constant term etc"""
        self.prev_pos, self.pos, self.next_pos = None, ipos, None  # (x, dx,...)
        self.prev_mass, self.mass, self.next_mass = None, imass, None  # (m, dm,...)
        self.prev_charge, self.charge, self.next_charge = None, icharge, None  # (q, dq,...)
        self.prev_radius, self.radius, self.next_radius = None, iradius, None  # (r, dr,...)
        self.prev_cohesion, self.cohesion, self.next_cohesion = None, icohesion, None  # (coh, dcoh,...)
        self.prev_alignment, self.alignment, self.next_alignment = None, ialignment, None  # (ali, dali,...)
        self.prev_separation, self.separation, self.next_separation = None, iseparation, None  # (sep, dsep,...)
        self.prev_view_radius, self.view_radius, self.next_view_radius = None, iview_radius, None  # (rad, drad,...)
        self.friction = friction  # linear friction
        self.elasticity = elasticity  # collision elasticity
        self.colour = colour  # (light, middle, dark)
        self.health = health
        self.damage = damage
        """nonzero masses have extra collision acceleration from any masses they are touching"""
        self.colliding_with = list(
        )  # list of objects currently being collided with

    @classmethod
    def update_cmp(cls, bodies, fast=False):
        cmp = Vector(0, 0)
        if len(bodies) != 0:
            cmm = 0
            for body in bodies:
                cmp += body.mass[0] * body.pos[0]
                cmm += body.mass[0]
            if cmm > 0:
                cmp /= cmm
        if fast:
            Body.cmp = cmp
        else:
            Body.cmp = (7 * Body.cmp + cmp) / 8

    @classmethod
    def update_bodies(cls, screen, dt, bodies, presses=None):
        """
        the main function of the class to iterate through its members updating their attributes in realtime
        """
        for body in bodies:
            body.update(screen, dt, bodies, presses)

        for body in bodies:
            body.step_next()

    """
    y -> dy -> y + dy = y2 -> dy2 -> (dy + dy2) / 2 = better dy -> y + dy = better y2 (VALID)
    y -> dy -> y + dy = y2 -> dy2 -> y2 + dy2 = y3 -> (y2 + y3) / 2 = better y2 (NOT VALID)
    (y3 - y) / 2 = better dy -> y + dy = better y2 = (y + y3) / 2
    """

    def update(self, screen, dt, bodies, presses):
        other_bodies = copy.copy(bodies)
        other_bodies.remove(self)
        self.set_next(screen, dt, other_bodies)

    def set_next(self, screen, dt, other_bodies):
        """
        calculates a body's attributes after a frame using its derivatives
        """
        self.next_pos = list()
        for i in range(max(len(self.pos) - 1, 2)):
            if len(self.pos) > 2 or i == 0:
                dxi = self.pos[i + 1] * dt
            else:
                dxi = Vector(0, 0)
            if i == 1:  # change in velocity
                for acc_func in self.acc_funcs:
                    # self.draw_vector(screen, acc_func(self, other_bodies))
                    a = acc_func(self, other_bodies)
                    dxi += a * dt
            self.next_pos.append(self.pos[i] + dxi)
        if len(self.pos) > 2:
            self.next_pos.append(self.pos[-1])

        self.next_mass = list()
        for i in range(len(self.mass) - 1):
            dxi = self.mass[i + 1] * dt
            self.next_mass.append(self.mass[i] + dxi)
        self.next_mass.append(self.mass[-1])

        self.next_charge = list()
        for i in range(len(self.charge) - 1):
            dxi = self.charge[i + 1] * dt
            self.next_charge.append(self.charge[i] + dxi)
        self.next_charge.append(self.charge[-1])

        self.next_radius = list()
        for i in range(len(self.radius) - 1):
            dxi = self.radius[i + 1] * dt
            self.next_radius.append(self.radius[i] + dxi)
        self.next_radius.append(self.radius[-1])

        self.next_cohesion = list()
        for i in range(len(self.cohesion) - 1):
            dxi = self.cohesion[i + 1] * dt
            self.next_cohesion.append(self.cohesion[i] + dxi)
        self.next_cohesion.append(self.cohesion[-1])

        self.next_alignment = list()
        for i in range(len(self.alignment) - 1):
            dxi = self.alignment[i + 1] * dt
            self.next_alignment.append(self.alignment[i] + dxi)
        self.next_alignment.append(self.alignment[-1])

        self.next_separation = list()
        for i in range(len(self.separation) - 1):
            dxi = self.separation[i + 1] * dt
            self.next_separation.append(self.separation[i] + dxi)
        self.next_separation.append(self.separation[-1])

    def step_next(self):
        """
        uses the calculated next attributes to shift to the next step
        """
        self.prev_pos, self.pos, self.next_pos = self.pos, self.next_pos, None
        self.prev_mass, self.mass, self.next_mass = self.mass, self.next_mass, None
        self.prev_charge, self.charge, self.next_charge = self.charge, self.next_charge, None
        self.prev_radius, self.radius, self.next_radius = self.radius, self.next_radius, None
        self.prev_cohesion, self.cohesion, self.next_cohesion = self.cohesion, self.next_cohesion, None
        self.prev_alignment, self.alignment, self.next_alignment = self.alignment, self.next_alignment, None
        self.prev_separation, self.separation, self.next_separation = self.separation, self.next_separation, None

    def get_grav_acc(self, other_bodies):
        a = Vector(0, 0)
        for other in other_bodies:
            distance = self.pos[0].distance_to(other.pos[0])
            if distance > 0:
                direction = (other.pos[0] - self.pos[0]).normalize()
                radii = self.radius[0] + other.radius[0]
                a += direction * c.GRAV * other.mass[0] * (
                    distance**2 + radii**4 * distance**-2)**-1
        return a

    def get_stat_acc(self, other_bodies):
        a = Vector(0, 0)
        if self.mass[0] != 0:
            for other in other_bodies:
                distance = self.pos[0].distance_to(other.pos[0])
                if distance > 0:
                    direction = (other.pos[0] - self.pos[0]).normalize()
                    radii = self.radius[0] + other.radius[0]
                    a += direction * c.STAT * self.charge[0] * other.charge[
                        0] / (distance**2 +
                              radii**4 * distance**-2) / self.mass[0]
        return a

    def get_mag_acc(self, other_bodies):
        a = Vector(0, 0)
        if self.mass[0] != 0:
            for other in other_bodies:
                distance = self.pos[0].distance_to(other.pos[0])
                if distance > 0:
                    radius = (self.pos[0] - other.pos[0]).normalize()
                    direction = self.pos[1].rotate(c.PI / 2)
                    radii = self.radius[0] + other.radius[0]
                    axb = other.pos[1].cross(radius)
                    a += direction * c.MAG * self.charge[0] * other.charge[
                        0] * axb / (distance**2 +
                                    radii**4 * distance**-2) / self.mass[0]
        return a

    def get_coh_acc(self, other_bodies):
        a = Vector(0, 0)
        sum_position = sum_cohesion = 0
        for other in other_bodies:
            distance = self.pos[0].distance_to(other.pos[0])
            if distance > 0 and distance <= self.view_radius[0]:
                sum_position += other.cohesion[0] * other.pos[0]
                sum_cohesion += other.cohesion[0]
        if sum_cohesion > 0:
            a = c.COH * sum_position / sum_cohesion - self.pos[0]
        return a

    def get_ali_acc(self, other_bodies):
        a = Vector(0, 0)
        sum_velocity = sum_alignment = 0
        for other in other_bodies:
            distance = self.pos[0].distance_to(other.pos[0])
            if distance > 0 and distance <= self.view_radius[0]:
                sum_velocity += other.alignment[0] * other.pos[1]
                sum_alignment += other.alignment[0]
        if sum_alignment > 0:
            a = c.ALI * sum_velocity / sum_alignment - self.pos[1]
        return a

    def get_sep_acc(self, other_bodies):
        a = Vector(0, 0)
        for other in other_bodies:
            distance = self.pos[0].distance_to(other.pos[0])
            if distance > 0 and distance <= self.view_radius[0]:
                direction = (other.pos[0] - self.pos[0]).normalize()
                radii = self.radius[0] + other.radius[0]
                a += direction * c.SEP * self.separation[0] * other.separation[
                    0] / distance**2
        return a

    def get_fric_acc(self, other_bodies):
        a = -self.pos[1] * self.friction
        return a

    def get_col_acc(self, other_bodies):
        a = Vector(0, 0)
        for other in other_bodies:
            if self.test_collision(other):
                selfpara, selfperp = self.pos[1].resolve_about(self.pos[0] -
                                                               other.pos[0])
                otherpara, otherperp = other.pos[1].resolve_about(self.pos[0] -
                                                                  other.pos[0])
                m1, m2 = self.mass[0], other.mass[0]
                selfparanew = (m1 - m2) / (m1 + m2) * selfpara + 2 * m2 / (
                    m1 + m2) * otherpara
                vf = selfparanew * self.elasticity + selfperp

                a += (vf - self.pos[1]) / c.DT

                if self.health is not None and other.damage is not None:
                    if isinstance(self, Player_class.Player) and isinstance(
                            other, Player_class.Player):
                        damage = round(other.damage *
                                       (self.pos[1] - other.pos[1]).norm /
                                       1200)
                        if other.pos[1].norm > self.pos[1].norm * 4:
                            damage *= 2
                    else:
                        damage = other.damage
                    self.health -= damage
                    if isinstance(self, Player_class.Player):
                        Body.damage_markers.append([
                            damage, self.pos[0] - Vector(20, 20),
                            other.colour[0],
                            max(min(damage, 100), 30)
                        ])

        return a

    def test_collision(self, other):
        distance = self.pos[0].distance_to(other.pos[0])
        if distance > 0:
            if distance <= self.radius[0] + other.radius[0]:
                if other not in self.colliding_with:
                    self.colliding_with.append(other)
                    return True
            elif other in self.colliding_with:
                self.colliding_with.remove(other)
        return False

    acc_funcs = [
        get_grav_acc, get_stat_acc, get_mag_acc, get_coh_acc, get_ali_acc,
        get_sep_acc, get_fric_acc, get_col_acc
    ]

    @classmethod
    def draw_bodies(cls, screen, bodies, fixed_to_screen=False, colour=None):
        for body in bodies:
            body.draw(screen, fixed_to_screen, colour)

        if not fixed_to_screen:
            for damage_marker in cls.damage_markers:
                damage_marker[3] -= 1
                if damage_marker[3] >= 0:
                    surface = pygame.font.SysFont("Consolas", 15).render(
                        str(damage_marker[0]), True, damage_marker[2])
                    screen.blit(
                        surface,
                        round(damage_marker[1] - Body.cmp +
                              Vector(*screen.get_size()) / 2))
                else:
                    cls.damage_markers.remove(damage_marker)

    def draw(self, screen, fixed_to_screen=False, colour=None):
        if self.pos is not None:
            if colour is None:
                colour = self.colour
            if fixed_to_screen:
                P = self.pos[0]
            else:
                P = self.pos[0] - Body.cmp
            pygame.draw.circle(screen, colour[0],
                               round(P + Vector(*screen.get_size()) / 2),
                               round(self.radius[0]))
            if self.radius[0] >= 3:
                pygame.draw.circle(screen, colour[1],
                                   round(P + Vector(*screen.get_size()) / 2),
                                   round(self.radius[0] - 2))

    def draw_vector(self, screen, vector):
        pygame.draw.line(screen, self.colour[1], round(self.pos[0]),
                         round(self.pos[0] + vector), 2)