def _bounce_one(a1, a2): # Bounces a1 dir_angle = vectors.angle(a2.pos, a1.pos) vel_angle = vectors.angle(a1.velocity) # If they are head on then we want to swivel them a little if vectors.bound_angle(dir_angle[0] + 180) == vel_angle[0]: dir_angle[0] = vectors.bound_angle(dir_angle[0] + 40) # Keep trying distances further and further apart until they're # not going to be overlapping any more overlapping = True dist = vectors.total_velocity(a1.velocity) a2_rect = (a2.pos[0], a2.pos[1], a2.size[0], a2.size[1]) while overlapping: new_pos = vectors.add_vectors(a1.pos, vectors.move_to_vector(dir_angle, dist)) new_rect = (new_pos[0], new_pos[1], a1.size[0], a1.size[1]) if not geometry.rect_collision(new_rect, a2_rect, True): overlapping = False dist += 1 # Add a bit to be safe new_pos = vectors.add_vectors( a1.pos, vectors.move_to_vector(dir_angle, dist + vectors.total_velocity(a1.velocity))) a1.pos = new_pos
def generate_bullet(self, target): # Set correct origin offset_angle = vectors.bound_angle( vectors.add_vectors(self._effect_offset_angle, self.facing)) origin_pos = vectors.add_vectors( self.get_offset_pos(use_effect_offset=True), self.actor.pos) # Get actual velocity we'll be using if type(target) == list or type(target) == tuple: direction = vectors.angle(origin_pos, target) target_pos = target else: direction = vectors.angle(origin_pos, target.pos) target_pos = target.pos velocity = vectors.move_to_vector(direction, self.bullet['velocity']) velocity[2] = math_lib.calc_trajectory( 0.1, vectors.distance(origin_pos, target_pos), self.bullet['velocity']) the_bullet = bullets.Shell( pos=origin_pos, velocity=velocity, image=self.bullet['image'], size=self.bullet['size'], blast_radius=self.bullet['blast_radius'], damage=self.bullet['damage'], dissipation_func=self.bullet.get('dissipation_func', "linear"), ) self.actor.bullets.append(the_bullet)
def _bounce_one(a1, a2): # Bounces a1 dir_angle = vectors.angle(a2.pos, a1.pos) vel_angle = vectors.angle(a1.velocity) # If they are head on then we want to swivel them a little if vectors.bound_angle(dir_angle[0]+180) == vel_angle[0]: dir_angle[0] = vectors.bound_angle(dir_angle[0] + 40) # Keep trying distances further and further apart until they're # not going to be overlapping any more overlapping = True dist = vectors.total_velocity(a1.velocity) a2_rect = (a2.pos[0], a2.pos[1], a2.size[0], a2.size[1]) while overlapping: new_pos = vectors.add_vectors(a1.pos, vectors.move_to_vector( dir_angle, dist )) new_rect = (new_pos[0], new_pos[1], a1.size[0], a1.size[1]) if not geometry.rect_collision(new_rect, a2_rect, True): overlapping = False dist += 1 # Add a bit to be safe new_pos = vectors.add_vectors(a1.pos, vectors.move_to_vector( dir_angle, dist + vectors.total_velocity(a1.velocity) )) a1.pos = new_pos
def generate_bullet(self, target): # Set correct origin offset_angle = vectors.bound_angle( vectors.add_vectors(self._effect_offset_angle, self.facing) ) origin_pos = vectors.add_vectors(self.get_offset_pos(use_effect_offset=True), self.actor.pos) # Get actual velocity we'll be using if type(target) == list or type(target) == tuple: direction = vectors.angle(origin_pos, target) target_pos = target else: direction = vectors.angle(origin_pos, target.pos) target_pos = target.pos velocity = vectors.move_to_vector(direction, self.bullet['velocity']) velocity[2] = math_lib.calc_trajectory(0.1, vectors.distance(origin_pos, target_pos), self.bullet['velocity']) the_bullet = bullets.Shell( pos=origin_pos, velocity=velocity, image = self.bullet['image'], size = self.bullet['size'], blast_radius = self.bullet['blast_radius'], damage = self.bullet['damage'], dissipation_func = self.bullet.get('dissipation_func', "linear"), ) self.actor.bullets.append(the_bullet)
def get_offset_pos(self, use_effect_offset=False): """Gets the drawing position relative to the centre of their actor""" if use_effect_offset: base_offset = self.get_offset_pos() own_offset = vectors.move_to_vector( angle=vectors.add_vectors(self._effect_offset_angle, self.actor.facing), distance=self._effect_offset_distance) return vectors.add_vectors(base_offset, own_offset) else: return vectors.move_to_vector(angle=vectors.add_vectors( self._img_offset_angle, self.actor.facing), distance=self._img_offset_distance)
def get_offset_pos(self, use_effect_offset=False): """Gets the drawing position relative to the centre of their actor""" if use_effect_offset: base_offset = self.get_offset_pos() own_offset = vectors.move_to_vector( angle = vectors.add_vectors(self._effect_offset_angle, self.actor.facing), distance = self._effect_offset_distance ) return vectors.add_vectors(base_offset, own_offset) else: return vectors.move_to_vector( angle = vectors.add_vectors(self._img_offset_angle, self.actor.facing), distance = self._img_offset_distance )
def generate_effect(self, target): offset_angle = vectors.bound_angle( vectors.add_vectors(self._effect_offset_angle, self.facing) ) origin_pos = vectors.add_vectors(self.get_offset_pos(use_effect_offset=True), self.actor.pos) the_effect = effects.Beam( origin=origin_pos, target=target.pos, colour=self.effect['colour'], duration=self.effect['duration'], degrade=self.effect.get("degrade", (0,0,0)), ) self.actor.effects.append(the_effect)
def generate_effect(self, target): offset_angle = vectors.bound_angle( vectors.add_vectors(self._effect_offset_angle, self.facing)) origin_pos = vectors.add_vectors( self.get_offset_pos(use_effect_offset=True), self.actor.pos) the_effect = effects.Beam( origin=origin_pos, target=target.pos, colour=self.effect['colour'], duration=self.effect['duration'], degrade=self.effect.get("degrade", (0, 0, 0)), ) self.actor.effects.append(the_effect)
def update(self): self.pos = vectors.add_vectors(self.pos, self.velocity) # Set rect self.rect.topleft = ( self.pos[0] - self.rect.width/2, self.pos[1] - self.rect.height/2 )
def get_rotated_offset(self, angle): if self.centre_offset == (0, 0): return 0, 0 # Get the actual angle to use offset_angle = vectors.bound_angle( vectors.add_vectors(self.centre_offset[1], angle)) return vectors.move_to_vector(offset_angle, self.centre_offset[0])
def generate_effect(self, target): colour = [self.effect['colour'][i] + random.random() * self.effect['variation'][i] for i in range(3)] the_effect = effects.Beam( origin=vectors.add_vectors(self.actor.pos, self.effect_offset), target=target.pos, colour=effects.bound_colour(colour), duration=self.required_charge-1, ) self.actor.effects.append(the_effect)
def test_add_vectors(self): vals = ( ([0, 0, 0], [1, 1, 1], [1, 1, 1]), ([2, 2, 2], [1, 1, 1], [3, 3, 3]), ([0, 0, 0], [0, 0, 0], [0, 0, 0]), ([3, 5, 7], [0, 5, 0], [3, 10, 7]), ) for a, b, expected in vals: self.assertEqual(vectors.add_vectors(a, b), expected)
def test_add_vectors(self): vals = ( ([0,0,0], [1,1,1], [1,1,1]), ([2,2,2], [1,1,1], [3,3,3]), ([0,0,0], [0,0,0], [0,0,0]), ([3,5,7], [0,5,0], [3,10,7]), ) for a, b, expected in vals: self.assertEqual(vectors.add_vectors(a, b), expected)
def get_rotated_offset(self, angle): if self.centre_offset == (0, 0): return 0, 0 # Get the actual angle to use offset_angle = vectors.bound_angle( vectors.add_vectors(self.centre_offset[1], angle) ) return vectors.move_to_vector(offset_angle, self.centre_offset[0])
def _bounce_both(a1, a2): # These are the angles directly away from each other angle1 = vectors.angle(a2.pos, a1.pos) angle2 = vectors.angle(a1.pos, a2.pos) vel_angle1 = vectors.angle(a1.velocity) vel_angle2 = vectors.angle(a2.velocity) # If they are head on then we want to swivel them a little if vel_angle1[0] == angle2[0] and vel_angle2[0] == angle1[0]: angle1[0] = vectors.bound_angle(angle1[0] + 20) angle2[0] = vectors.bound_angle(angle2[0] + 20) # Keep trying distances further and further apart until they're # not going to be overlapping any more overlapping = True dist_multiplier = 0.1 while overlapping: dist_multiplier += 0.1 new_pos1 = vectors.add_vectors( a1.pos, vectors.move_to_vector(angle1, max(a1.size) * dist_multiplier)) new_pos2 = vectors.add_vectors( a2.pos, vectors.move_to_vector(angle2, max(a2.size) * dist_multiplier)) new_rect1 = (new_pos1[0], new_pos1[1], a1.size[0], a1.size[1]) new_rect2 = (new_pos2[0], new_pos2[1], a2.size[0], a2.size[1]) if not geometry.rect_collision(new_rect1, new_rect2): overlapping = False a1.pos = new_pos1 a2.pos = new_pos2
def generate_effect(self, target): colour = [ self.effect['colour'][i] + random.random() * self.effect['variation'][i] for i in range(3) ] the_effect = effects.Beam( origin=vectors.add_vectors(self.actor.pos, self.effect_offset), target=target.pos, colour=effects.bound_colour(colour), duration=self.required_charge - 1, ) self.actor.effects.append(the_effect)
def _bounce_both(a1, a2): # These are the angles directly away from each other angle1 = vectors.angle(a2.pos, a1.pos) angle2 = vectors.angle(a1.pos, a2.pos) vel_angle1 = vectors.angle(a1.velocity) vel_angle2 = vectors.angle(a2.velocity) # If they are head on then we want to swivel them a little if vel_angle1[0] == angle2[0] and vel_angle2[0] == angle1[0]: angle1[0] = vectors.bound_angle(angle1[0] + 20) angle2[0] = vectors.bound_angle(angle2[0] + 20) # Keep trying distances further and further apart until they're # not going to be overlapping any more overlapping = True dist_multiplier = 0.1 while overlapping: dist_multiplier += 0.1 new_pos1 = vectors.add_vectors(a1.pos, vectors.move_to_vector( angle1, max(a1.size) * dist_multiplier )) new_pos2 = vectors.add_vectors(a2.pos, vectors.move_to_vector( angle2, max(a2.size) * dist_multiplier )) new_rect1 = (new_pos1[0], new_pos1[1], a1.size[0], a1.size[1]) new_rect2 = (new_pos2[0], new_pos2[1], a2.size[0], a2.size[1]) if not geometry.rect_collision(new_rect1, new_rect2): overlapping = False a1.pos = new_pos1 a2.pos = new_pos2
def logic_cycle(self): """The core function of the sim, this is where the 'magic happens'""" if int(time.time()) != self.cycle_count[1]: self.cycle_count = [0, int(time.time())] self.next_ai_update -= 1 if self.next_ai_update <= 0: self.update_ai_queues() self.next_ai_update = 30 self.read_ai_queues() self.tick += 1 self.orders[self.tick + self.tick_jump] = [] self.q_orders[self.tick + self.tick_jump] = [] self.issue_orders() # This will warn us if the sim is lagging behind how fast it's meant to be time_over = time.time() - self.next_cycle if time_over > 0.25: print("Logic lag of %ss" % time_over) # Update the AIs for t, a in self.autotargeters.items(): a.update() # Update the actors themselves to_remove = [] to_add = [] for i, a in enumerate(self.actors): # First we need to check to see if it's got a build order # it'd have one because the AI can't directly tell us # to build something and instantly be told what the building # item is, thus we place it here and automatically tell # the actor to go build it if a.current_order[0] == "build" and a.completion >= 100: cmd, pos, type_name = a.current_order # Should this be done via a build queue instead? if self.actor_types[a.actor_type]['uses_build_queue']: a.build_queue.append(type_name) else: a_type = self.actor_types[type_name] new_rect = pygame.Rect(pos[0], pos[1], a_type['size'][0], a_type['size'][1]) building_rect = ai_lib.place_actor(self.actors, new_rect, [50, 100, 200], self.battlefield['size']) if building_rect != None: posx = building_rect.left + building_rect.width/2 posy = building_rect.top + building_rect.height/2 to_add.append((a, { "type": type_name, "pos": [posx, posy, 0], "team": a.team, "order_queue": list(a.rally_orders), })) a.next_order() a.update() # Is the actor trying to place a new unit? # We only check as often as we check for collisions, this gives a cycle # for an already started actor to be given a position as it defaults to 0,0 # and this has an impact on it's rect if self._collision_inverval_count < 2: if a.build_queue != [] and a.completion >= 100: a_type = self.actor_types[a.build_queue[0]] new_rect_pos = vectors.add_vectors(a.pos, a.build_offset) new_rect = pygame.Rect(new_rect_pos[0], new_rect_pos[1], a_type['size'][0], a_type['size'][1]) if not sim_lib.test_possible_collision(self.actors, new_rect): to_add.append((a, { "type": a.build_queue[0], "pos": new_rect_pos, "team": a.team, "order_queue": list(a.rally_orders), })) self.signal_menu_rebuild = True del(a.build_queue[0]) if a.hp <= 0: to_remove.insert(0, i) for i in to_remove: del(self.actors[i]) for builder, new_actor in to_add: new_target = self.place_actor(new_actor) builder.issue_command("aid", target=new_target) # Bullets too to_delete = [] for i, b in enumerate(self.bullets): b.update() if b.dead: new_effect = b.explode(self.actors) if new_effect != None: self.effects.append(new_effect) to_delete.insert(0, i) for i in to_delete: del(self.bullets[i]) # And lastly effects to_delete = [] for i, e in enumerate(self.effects): e.update() if e.dead: to_delete.insert(0, i) continue # Delete any uneeded effects for i in to_delete: del(self.effects[i]) # Check for collisions self._collision_inverval_count -= 1 if self._collision_inverval_count < 1: self._collision_inverval_count = self._collision_interval collisions = sim_lib.get_collisions(self.actors) # We now have a list of all the collisions for obj1, obj2 in collisions: # We order them based on aid, this way we always deal with the same # actor in the same way and the order the collison was found is # irrelevant actor_lib.handle_pathing_collision(min(obj1, obj2), max(obj1, obj2)) # Set next cycle time self.next_cycle = time.time() + self._cycle_delay self.cycle_count[0] += 1