def __init__(self, main, size=None, screen=None, zoom_offset=1, zoom_constraint=None): self.main = main self.size = size if size is not None else [6000, 6000] self.screen = pygame.Rect( screen if screen is not None else [0, 0, 800, 600]) self.update_rect = pygame.Rect(0, 0, *self.size) z_const = zoom_constraint if zoom_constraint else [None, 4] self.camera = Camera((0, 0), self.screen, self.get_world_rect(), z_const, zoom_offset=zoom_offset) self.visible = self.camera.get_rect() self.player = None self.step_time = 1 self.space_time_coef = 1 self.event_system = None self.phys_group = None self.gui = None self.pressed = [] self.paused = False self.mouse_window_prev = Vec2d(0, 0) self.mouse_world_prev = Vec2d(0, 0) self.mouse_window = Vec2d(0, 0) self.mouse_world = Vec2d(0, 0)
def __init__(self, parent, position=None, angle=0, allowed=True, top=True): """ :param parent: PhysObject :param position: (x, y) in local coords :param angle: float (degrees) angle in local coords. :param allowed: [config.ROLE, ...] | None | True :param top: bool - is object above creature or not. """ self.parent = parent self._pos = Vec2d(0, 0) if position is None else Vec2d(position) self._ang = angle self.allowed = True if allowed is True else ([] if allowed is None else list(allowed)) self.object = None self.role = None self.top = top
def __init__(self, *args, **kwargs): """ Necessary assignment - rect - image """ self._pos = Vec2d(0, 0) self._size = Vec2d(0, 0) self._angle = 0 self._image = None self.damping = 0 self.step_time = 1 super().__init__(*args, **kwargs) self.play_sound('creation')
class ImageHandler(PhysObject): """ Sprite with GObject image in class attributes for RAM economy """ size_inc = 1 _frames = [] IMAGE_SHIFT = Vec2d(0, 0) def __init__(self, *args, obj=None, **kwargs): super().__init__(*args, **kwargs) if obj is None: self._image = GObject(self._frames) else: self._image = GObject(obj) def end_step(self): super().end_step() self._image.update(self.step_time) @classmethod def image_to_local(cls, pos): return Vec2d(pos) * cls.size_inc + cls.IMAGE_SHIFT def _get_image(self): return self._image.read()
class Weapon(YTGBaseWeapon): size_inc = 1 max_health = 50 proj_velocity = 1000 fire_delay = 1000 fire_pos = Vec2d(0, 0) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.i_body = pymunk.Body() self.shape = pymunk.Poly(self.body, self.POLY_SHAPE) self.shape.density = 1 @classmethod def init_class(cls): cls._frames, cls.IMAGE_SHIFT = cast_model(MODEL, CS, cls.size_inc) cls.precalculate_shape() cls.calculate_poly_shape() @classmethod def precalculate_shape(cls): radius = 10 cls.RADIUS = radius * cls.size_inc @classmethod def calculate_poly_shape(cls): img_poly_left = [] poly_left = [tuple(e[n] - CS[n] for n in range(2)) for e in img_poly_left] poly_right = [(e[0], -e[1]) for e in poly_left[::-1]] cls.POLY_SHAPE = [(e[0] * cls.size_inc, e[1] * cls.size_inc) for e in poly_left + poly_right]
def local_to_world(self, pos): """ Конвертация из локальной системы координат в уровневую. Положение точки в локальной системе не зависит от углов наклона тела к глобальным осям. :param pos: (x, y) :return: Vec2d(x, y) """ return self.position + Vec2d(pos).rotated(self.angle)
def world_to_local(self, pos): """ Конвертация из уровневой системы координат в локальную. Положение точки в локальной системе не зависит от углов наклона тела к глобальным осям. :param pos: (x, y) :return: Vec2d(x, y) """ return (Vec2d(pos) - self.position).rotated(-self.angle)
def __init__(self): """ Necessary assignment - rect - image """ super().__init__() self._image = None self._pos = Vec2d(0, 0) self._size = Vec2d(0, 0) self._angle = 0 self.step_time = 1 self.age = 0 self.play_sound('creation')
def draw(self, surface, camera): cam_rect = camera.get_rect() cam_bb = pymunk.BB(cam_rect.left, cam_rect.top, cam_rect.right, cam_rect.bottom) cam_tl = Vec2d(cam_rect.topleft) cam_offset = Vec2d(self.draw_offset) zoom = camera.get_current_zoom() blit = surface.blit sprite_dict = self.spritedict for sprite in self.layer_sorted(): if sprite.bb.intersects(cam_bb): s_img = sprite.image img = pygame.transform.rotozoom(s_img, -sprite.angle, zoom) img_size = img.get_size() s_pos = sprite.position sc = (int(cam_offset[0] + (s_pos[0] - cam_tl[0]) * zoom - img_size[0] / 2), int(cam_offset[1] + (s_pos[1] - cam_tl[1]) * zoom - img_size[1] / 2)) sprite_dict[sprite] = blit(img, sc) cam_c = Vec2d(cam_rect.center) cam_h = (CAMERA_SOUND_HEIGHT / zoom)**2 for snd in self.sounds: sound = snd[0] kwargs = snd[2] c = kwargs.get('channel', None) if c is not None: c = pygame.mixer.Channel(c) c.play(sound) else: c = sound.play(kwargs.get('loops', 0), kwargs.get('max_time', 0), kwargs.get('fade_ms', 0)) if c is not None: to_s = snd[1].position - cam_c d = to_s.get_length_sqrd() v = SOUND_COEF / (math.sqrt(d + cam_h) + 1) c.set_volume(v) self.sounds.clear() self.lostsprites = []
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._pos = Vec2d(0, 0) self._ang = 0 self._parent = None self._i_shape = None self._i_body = None self.activated = False
def force_fire(self, **kwargs): self.play_sound('fire') proj = self.spawn_proj() ang = self.miss_angle() rad = math.radians(ang) vel = self.proj_velocity vec = Vec2d(vel * math.cos(rad), vel * math.sin(rad)) proj.velocity = vec proj.angle = ang
def set_local_placement(self, pos, ang): """ Установить позицию и угол наклона относительно родителя. :param pos: (x, y) :param ang: float (degrees) """ self._pos = Vec2d(pos) self._ang = ang self.update_local_placement()
def calculate_mouse_world(self): """ Не рекомендуется к использованию - функция перерасчёта. Её результат записывается в Level.mouse_world :return: Vec2d(x, y) """ ms = pygame.mouse.get_pos() sc, vs = self.screen.size, self.visible.size zoom = max((sc[0] / vs[0], sc[1] / vs[1])) return (Vec2d(ms) - self.screen.topleft) / zoom + self.visible.topleft
def cast_frames(source, centers, size_incs): """ Обрезать по содержимому, центрировать и масштабировать кадры. :param source: pygame.Surface :param centers: [(x, y), ...] :param size_incs: [int, ...] :return: [pygame.Surface, ...], (x, y) - image shift """ frames = [] c0 = Vec2d(0, 0) for n, f in enumerate(source): i_size = f.get_size() center = centers[n] size_inc = size_incs[n] if center is None: center = [e / 2 for e in i_size] else: if center[0] is None: center[0] = i_size[0] / 2 if center[1] is None: center[1] = i_size[1] / 2 b_rect = f.get_bounding_rect() b_size = ceil(Vec2d(b_rect.size) * size_inc) h_size = b_size / 2 img_center = ceil((Vec2d(center) - b_rect.topleft) * size_inc) if n == 0: c0 = -img_center inc_vector = abs(img_center - h_size) hf_size = h_size + inc_vector tl = hf_size - img_center bs = f.subsurface(b_rect) img = pygame.Surface(hf_size * 2).convert_alpha() img.fill((255, 255, 255, 0)) img.blit(pygame.transform.scale(bs, b_size), tl) if DEBUG.DRAW: r = img.get_rect() r.w -= 2 r.h -= 2 pygame.draw.rect(img, (255, 0, 0), r, 2) frames.append(img) return frames, c0
def force_fire(self, **kwargs): self.play_sound('fire') ang = self.angle + (self.fire_n - 1) * self.inaccuracy * 180 proj = self.spawn_proj() proj.velocity = Vec2d.from_anglen(ang, self.proj_velocity) proj.angle = self.angle proj.target = kwargs.get('target_function', kwargs.get('target', None)) self.fire_n = (self.fire_n + 1) % 3
def cast_image(source, center=None, size_inc=1): """ Обрезать по содержимому, центрировать и масштабировать изображение. :param source: pygame.Surface :param center: (x, y) :param size_inc: int :return: pygame.Surface, (x, y) - image shift """ i_size = source.get_size() if center is None: center = [e / 2 for e in i_size] else: if center[0] is None: center[0] = i_size[0] / 2 if center[1] is None: center[1] = i_size[1] / 2 # Область, которую есть смысл отображать. b_rect = source.get_bounding_rect() # Её размер после масштабирования. b_size = ceil(Vec2d(b_rect.size) * size_inc) # Половина. h_size = b_size / 2 # Центр обрезанного и масштабированного изображения относительно просто обрезанного. img_center = ceil((Vec2d(center) - b_rect.topleft) * size_inc) # Вектор масштабирования, вдоль которого растягивается новая поверхность, # чтобы центрированное изображение вместилось. inc_vector = abs(img_center - h_size) hf_size = h_size + inc_vector tl = hf_size - img_center bs = source.subsurface(b_rect) img = pygame.Surface(hf_size * 2).convert_alpha() img.fill((255, 255, 255, 0)) img.blit(pygame.transform.scale(bs, b_size), tl) if DEBUG.DRAW: r = img.get_rect() r.w -= 2 r.h -= 2 pygame.draw.rect(img, (255, 0, 0), r, 2) return img, -img_center
def update(self): if self.boost: self.body.apply_force_at_local_point((5000000, 0), (0, 0)) if self.target is not None: if hasattr(self.target, '__call__'): tp = self.target() else: tp = list(self.target) da = angular_distance(self.angle, (Vec2d(tp) - self.position).angle) self.angle += da * .1
def calculate_global_rect(self): """ Абсолютная позиция (прямоугольник) относительно области отрисовки. Рекомендуется вызывать только в Division.start_step, используйте get_global_rect :return: Rect() """ if self._parent is not None: rect = self._parent.global_rect elif self._main is not None: rect = self._main.get_visible_rect() else: return FRect(self._rect) if self.absolute_position: return FRect(*map(sum, zip(rect.topleft, self._rect.topleft)), *self._rect.size) else: s_rect = self._rect s_tl = Vec2d(s_rect.topleft) s_sz = Vec2d(s_rect.size) return FRect(*(s_tl / 100 * rect.size + rect.topleft), *(s_sz / 100 * rect.size))
def update_local_placement(self): """ Обновляет относительное положение объекта (Изменяет текущий shape в соответствии с локальной позицей). """ pos, ang = self.local_pos, self.local_angle i_shape = self._i_shape shape = self._shape if isinstance(i_shape, pymunk.Poly): shape.unsafe_set_vertices([e + pos for e in i_shape.get_vertices()]) elif isinstance(i_shape, pymunk.Segment): shape.unsafe_set_endpoints(i_shape.a + pos, i_shape.b + pos) elif isinstance(i_shape, pymunk.Circle): shape.unsafe_set_offset(Vec2d(pos) + self._i_shape.offset)
def force_fire(self, **kwargs): """ Совершает выстрел независимо от обстоятельств. :param kwargs: Иногда нужен для прицеливания дочерним классам. """ self.play_sound('fire') proj = self.spawn_proj() ang = self.miss_angle() rad = math.radians(ang) vel = self.proj_velocity vec = Vec2d(vel * math.cos(rad), vel * math.sin(rad)) proj.velocity = vec proj.angle = ang
def force_fire(self, **kwargs): self.play_sound('fire') ca = self.angle da = self.inaccuracy * 360 sa = ca - da / 2 frag = self.fragmentation vel = self.proj_velocity segments = [] b_a = self.spawn(Ballast) b_a.set_parent(self) b_a.angle = sa b_a.velocity = Vec2d.from_anglen(sa, vel) b_b = self.spawn(Ballast) b_b.set_parent(self) b_b.angle = sa + da b_b.velocity = Vec2d.from_anglen(sa + da, vel) b_a.pair = b_b b_b.pair = b_a w = (self.Projectile.LENGTH - self.Projectile.RADIUS) / 2 for n in range(frag): proj = self.spawn_proj() if segments: Pivot(proj.body, segments[-1].body, (-w, 0), (w, 0)) segments.append(proj) ang = sa + da * (n / frag) proj.angle = ang - 90 proj.velocity = Vec2d.from_anglen(ang, vel * .8) Pivot(b_a.body, segments[0].body, (0, 0), (-w, 0)) Pivot(b_b.body, segments[-1].body, (0, 0), (w, 0))
def send_event(self, event): super().send_event(event) if event.type == pygame.KEYDOWN and event.key == pygame.K_j: ind = self.w_inv.index for n in range(len(self.w_inv[ind])): w = self.w_inv.drop(ind) ang = math.radians(self.angle) vector = Vec2d(math.cos(ang), math.sin(ang)) mv = 1000000 / w.mass vel = w.velocity_for_distance( (self.level.mouse_world - self.position).length) if vel > mv: vel = mv w.position += vector * 75 w.velocity = vector * vel
def start_step(self, upd_time, time_coef=1): if not self.paused: self.step_time = upd_time * time_coef self.pressed = pygame.key.get_pressed() self.mouse_window_prev = self.mouse_window self.mouse_world_prev = self.mouse_world self.mouse_window = Vec2d(pygame.mouse.get_pos()) self.mouse_world = self.calculate_mouse_world() if self.event_system: self.event_system.start_step(upd_time) if self.player is not None: tp = self.mouse_window - self.screen.center self.camera.center = self.player.position + tp if self.gui is not None: self.gui.start_step(upd_time)
def force_fire(self, **kwargs): self.play_sound('fire') proj = self.spawn_proj() ang = self.miss_angle() rad = math.radians(ang) if 'target' in kwargs.keys(): dis = (proj.position - kwargs['target']).length vel = proj.velocity_for_distance(dis, proj.life_left) if vel > self.proj_velocity: vel = self.proj_velocity else: vel = self.proj_velocity vec = Vec2d(vel * math.cos(rad), vel * math.sin(rad)) proj.velocity = vec proj.angle = ang
def update_local_placement(self): """ Устанавливает форму объекта в соответсвтие с локальным положением :return: """ pos, ang = self.local_pos, self.local_angle i_shape = self._i_shape shape = self._shape if isinstance(i_shape, pymunk.Poly): shape.unsafe_set_vertices( [e + pos for e in i_shape.get_vertices()]) elif isinstance(i_shape, pymunk.Segment): shape.unsafe_set_endpoints(i_shape.a + pos, i_shape.b + pos) elif isinstance(i_shape, pymunk.Circle): shape.unsafe_set_offset(Vec2d(pos) + self._i_shape.offset)
def walk(self, vec): cv = self.velocity if any(vec): tv = Vec2d(vec) tv.length = self.max_vel dif = tv - cv dif.length = self.engine_force self._body.force += dif self.working = True elif abs(cv[0]) > .01 and abs(cv[1] > .01): # stopping cv.length = self.engine_force self._body.force -= cv else: self.velocity = (0, 0)
def walk(self, vec): cv = self.velocity if any(vec): tv = Vec2d(vec) tv.length = self.max_vel dif = tv - cv if any(dif): dif.length = self.walk_force self._body.force += dif elif abs(cv[0]) > .01 or abs(cv[1]) > .01: # На входе (0, 0) вектор. Замедляемся. cv.length = self.walk_force self._body.force -= cv else: # Скорость пренебрежимо мала self.velocity = (0, 0)
class BaseWeapon(BaseComponent): draw_layer = DRAW_LAYER.WEAPON role = ROLE.WEAPON fire_pos = Vec2d(0, 0) proj_velocity = 1000 fire_delay = 1000 inaccuracy = .02 Projectile = None def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.recharge = 0 def end_step(self): super().end_step() if self.recharge > 0: self.recharge -= self.step_time def shot(self, **kwargs): if self.recharge <= 0: self.force_fire(**kwargs) self.recharge = self.fire_delay def spawn(self, cls): obj = cls() obj.add(*self.groups()) obj.position = self.local_to_world(self.fire_pos) return obj def spawn_proj(self): if self.Projectile: proj = self.spawn(self.Projectile) proj.set_parent(self) return proj def miss_angle(self): return self.angle + 360 * (random.random() - .5) * self.inaccuracy def force_fire(self, **kwargs): self.play_sound('fire') proj = self.spawn_proj() ang = self.miss_angle() rad = math.radians(ang) vel = self.proj_velocity vec = Vec2d(vel * math.cos(rad), vel * math.sin(rad)) proj.velocity = vec proj.angle = ang
def walk(self, vec): cv = self.velocity if any(vec): tv = Vec2d(vec) tv.length = self.max_vel dif = tv - cv dif.length = self.walk_force self._body.force += dif elif abs(cv[0]) > .01 or abs(cv[1]) > .01: # stopping cv.length = self.walk_force self._body.force -= cv else: self.velocity = (0, 0) if 'nan' in str(self.position): print(self.position) print(self.velocity, self._body.force, self.position)
class StaticImage(BaseSprite): draw_layer = DRAW_LAYER.VFX size_inc = 1 _frames = [] IMAGE_SHIFT = Vec2d(0, 0) def __init__(self, *args, obj=None, **kwargs): super().__init__(*args, **kwargs) if obj is None: self._image = GObject(self._frames) else: self._image = GObject(obj) def end_step(self): super().end_step() self._image.update(self.step_time) @classmethod def image_to_local(cls, pos): return Vec2d(pos) * cls.size_inc + cls.IMAGE_SHIFT def _get_image(self): return self._image.read()