import pygame from utils import remove_from_list import base import sprite_types import inspect class Rect(object): """Pythonic rect wrapper, using in-built Rect for speed""" map_class = "Rect" def __init__(self, center=(None, None), size=(None, None), color=None, container_obj=None): if not (len(center) == 2): raise AttributeError("Center must be (x,y)") x = center[0] y = center[1] if (not (len(size) == 2)) or (None in size): raise AttributeError( "size must be provided and is of format (w,h)") self.color = color if not container_obj: if (x is None) or (y is None): raise AttributeError("Center=(x,y) must be provided") else: self.container_obj = None self.center = (x, y) else: self.container_obj = container_obj # Use weakrefs tmp_left = self.center[0] - size[0] / 2 tmp_top = self.center[1] - size[1] / 2 self._rect = pygame.Rect((tmp_left, tmp_top), size) self.sync_self() @classmethod def from_rect(cls, rect): # Doesnt use the same rect, effectively makes a copy # As rects cant have extra props, it shouldnt impose any limitations. return cls(rect.center, rect.size) @classmethod def from_top_left(cls, top_left=(None, None), size=(None, None), color=None, container_obj=None): if not (None in top_left): tmp_center = top_left[0] + size[0] / 2, top_left[1] + size[1] / 2 else: tmp_center = top_left return cls(tmp_center, size, color, container_obj) @property def center(self): if self.container_obj: return (self.container_obj.x, self.container_obj.y) else: return self._center @center.setter def center(self, val): if self.container_obj: self.container_obj.x, self.container_obj.y = val else: self._center = val @classmethod def from_container_obj(cls, container_obj, size, color=None): return cls(size=size, color=color, container_obj=container_obj) @property def center_x(self): return self.center[0] @property def center_y(self): return self.center[1] @center_x.setter def center_x(self, new_x): self.center[0] = new_x @center_y.setter def center_y(self, new_y): self.center[1] = new_y @property def x(self): return self.center_x @x.setter def x(self, val): self.center_x = val @property def y(self): return self.center_y @y.setter def y(self, val): self.center_y = val @property def bbox(self): return self.inner_rect.copy() @property def inner_rect(self): self.sync_inner_rect() return self._rect def sync_inner_rect(self): self._rect.center = self.center def sync_self(self): self.center = self._rect.center # misc. methods def __repr__(self): return "{0.__class__}( center={0.center}, size={0.size}, color={0.color})".format(self) def __copy__(self): return self.__class__(**self.get_props()) def get_props(self): ''' Returns all of the constructor params, reqd for creationg a copy ''' return { "center": self.center, "size": self.size, "color": self.color, "container_obj": self.container_obj, } def __eq__(self, another_rect): if self.get_props() == another_rect.get_props(): return True else: return False # draw and blit func def blit(self, surface, (x, y)=(None, None), color=None, clip_rect=None, width=1, use_antialiasing=False): if (x, y) == (None, None): (x, y) = self.center # pygame.Rect does this, which is used in clear_rect x, y = int(x), int(y) if not color: if self.color: color = self.color else: color = pygame.Color("blue") if clip_rect: # Set clip region surface.set_clip(clip_rect) if not use_antialiasing: pygame.draw.rect( surface, color, self.bbox, width) else: # do later pass if clip_rect: # Remove clip region surface.set_clip(None) def draw(self): tmp_dirty_sprite = { "sprite": self, # later shape may be used for matching insted of sprites, as sprite # may be subclassed, changing "sprite" obj "shape": "Rect", # Very IMP: If using Rect for bbox, use rect_to_pygame_rect like # below. "bbox": self.bbox, "config": { "center": self.center, "size": self.size, "color": self.color } } for tmp_old_sprite in self.container_obj.list_rendered_sprite: # if any of them is eqv. to new dirty sprite, mark old one as # still_valid if tmp_old_sprite == tmp_dirty_sprite: # puts "still_valid" entry in the old_dirty_sprite_dict # quite good approach, wont match with anything again # but "still_valid" should be removed, before next step (tick), to allow # checks later tmp_old_sprite["still_valid"] = True break else: # i.e. if no match found, list it as dirty sprite self.container_obj.list_dirty_sprite.append(tmp_dirty_sprite) # collide func def collide(self, other_obj, moved_center=(None, None), from_container_obj=False): ''' Collision handler for any type of obj''' # todo: handle game_class=="all" # todo: point class if not hasattr(other_obj, "__class__"): raise AttributeError( "other_obj must be a class or instance") # Handle instances if isinstance(other_obj, sprite_types.Circle): return self.collide_circle(other_obj, moved_center, from_container_obj) elif isinstance(other_obj, Rect): return self.collide_rect(other_obj, moved_center, from_container_obj) elif isinstance(other_obj, sprite_types.Img): return self.collide_img(other_obj, moved_center, from_container_obj) else: pass # Handle classes if inspect.isclass(other_obj): if issubclass(other_obj, sprite_types.Circle): return self.collide_list_circle(other_obj.list_instance, moved_center, from_container_obj) elif issubclass(other_obj, Rect): return self.collide_list_rect(other_obj.list_instance, moved_center, from_container_obj) elif issubclass(other_obj, sprite_types.Img): return self.collide_list_img(other_obj.list_instance, moved_center, from_container_obj) else: pass # Handle fun_Game and fun_Class or their instances if isinstance(other_obj, base.fun_Game) or (inspect.isclass(other_obj) and issubclass(other_obj, base.fun_Game)): def instance_collide(other_obj): return self.collide(other_obj.sprite, moved_center, from_container_obj=True) to_return = other_obj.for_all_instance( instance_collide, flatten="level-class") remove_from_list(to_return, self.container_obj) remove_from_list(to_return, None, recursive=True) remove_from_list(to_return, False, recursive=True) return to_return if inspect.isclass(other_obj) and issubclass(other_obj, base.fun_Class): def instance_collide(other_obj): return self.collide(other_obj.sprite, moved_center, from_container_obj=True) to_return = other_obj.for_all_instance( instance_collide, flatten=False) remove_from_list(to_return, self.container_obj) remove_from_list(to_return, False, recursive=True) remove_from_list(to_return, None, recursive=True) remove_from_list(to_return, False, recursive=True) return to_return if isinstance(other_obj, base.fun_Class): other_obj_sprite = other_obj.sprite to_return = self.collide( other_obj_sprite, moved_center, from_container_obj=True) # doesnt return list, so prev. case operations not done return to_return # Rest are all synced props and methods # w, h, width, height # top, left, bottom, right # topleft, bottomleft, topright, bottomright # midtop, midleft, midbottom, midright # size # Getters first @property def width(self): return self.bbox.width @property def height(self): return self.bbox.height @property def w(self): return self.bbox.w @property def h(self): return self.bbox.h @property def top(self): return self.bbox.top @property def left(self): return self.bbox.left @property def bottom(self): return self.bbox.bottom @property def right(self): return self.bbox.right @property def topleft(self): return self.bbox.topleft @property def bottomleft(self): return self.bbox.bottomleft @property def topright(self): return self.bbox.topright @property def bottomright(self): return self.bbox.bottomright @property def midtop(self): return self.bbox.midtop @property def midleft(self): return self.bbox.midleft @property def midbottom(self): return self.bbox.midbottom @property def midright(self): return self.bbox.midright @property def size(self): return self.bbox.size # Then setters @width.setter def width(self, val): self.inner_rect.width = val self.sync_self() @height.setter def height(self, val): self.inner_rect.height = val self.sync_self() @w.setter def w(self, val): self.inner_rect.w = val self.sync_self() @h.setter def h(self, val): self.inner_rect.h = val self.sync_self() @top.setter def top(self, val): self.inner_rect.top = val self.sync_self() @left.setter def left(self, val): self.inner_rect.left = val self.sync_self() @bottom.setter def bottom(self, val): self.inner_rect.bottom = val self.sync_self() @right.setter def right(self, val): self.inner_rect.right = val self.sync_self() @topleft.setter def topleft(self, val): self.inner_rect.topleft = val self.sync_self() @bottomleft.setter def bottomleft(self, val): self.inner_rect.bottomleft = val self.sync_self() @topright.setter def topright(self, val): self.inner_rect.topright = val self.sync_self() @bottomright.setter def bottomright(self, val): self.inner_rect.bottomright = val self.sync_self() @midtop.setter def midtop(self, val): self.inner_rect.midtop = val self.sync_self() @midleft.setter def midleft(self, val): self.inner_rect.midleft = val self.sync_self() @midbottom.setter def midbottom(self, val): self.inner_rect.midbottom = val self.sync_self() @midright.setter def midright(self, val): self.inner_rect.midright = val self.sync_self() @size.setter def size(self, val): self.inner_rect.size = val self.sync_self() # in-place methods def move_ip(self, *args, **kwargs): self.inner_rect.move_ip(*args, **kwargs) self.sync_self() return self def inflate_ip(self, *args, **kwargs): self.inner_rect.inflate_ip(*args, **kwargs) self.sync_self() return self def clamp_ip(self, *args, **kwargs): self.inner_rect.clamp_ip(*args, **kwargs) self.sync_self() return self def normalize_ip(self): self.inner_rect.normalize() self.sync_self() return self def union_ip(self, *args, **kwargs): self.inner_rect.union_ip(*args, **kwargs) self.sync_self() return self def unionall_ip(self, *args, **kwargs): self.inner_rect.unionall_ip(*args, **kwargs) self.sync_self() return self # non in-place methods def copy(self, *args, **kwargs): return self.bbox.copy(*args, **kwargs) def move(self, *args, **kwargs): return self.bbox.move(*args, **kwargs) def inflate(self, *args, **kwargs): return self.bbox.inflate(*args, **kwargs) def clamp(self, another_rect): if hasattr(another_rect, "bbox"): tmp_rect = another_rect.bbox else: tmp_rect = another_rect return self.bbox.clamp(tmp_rect) def clip(self, another_rect): if hasattr(another_rect, "bbox"): tmp_rect = another_rect.bbox else: tmp_rect = another_rect return self.bbox.clip(tmp_rect) def union(self, another_rect): if hasattr(another_rect, "bbox"): tmp_rect = another_rect.bbox else: tmp_rect = another_rect return self.bbox.union(tmp_rect) def unionall(self, rect_sequence): new_rect_seq = [] for another_rect in rect_sequence: if hasattr(another_rect, "bbox"): tmp_rect = another_rect.bbox else: tmp_rect = another_rect new_rect_seq.append(tmp_rect) return self.bbox.unionall(new_rect_seq) def fit(self, another_rect): if hasattr(another_rect, "bbox"): tmp_rect = another_rect.bbox else: tmp_rect = another_rect return self.bbox.fit(tmp_rect) def contains(self, another_rect): if hasattr(another_rect, "bbox"): tmp_rect = another_rect.bbox else: tmp_rect = another_rect return self.bbox.contains(tmp_rect) def collide_point(self, *args, **kwargs): ''' collide_point(x,y) -> return bool collide_point((x,y)) -> return bool ''' return self.bbox.collidepoint(*args, **kwargs) def collide_rect(self, another_rect, moved_center=(None, None), from_container_obj=False): if not (len(moved_center) == 2): print "moved_center must be of the form (x,y)" _center = self.center moved_center = list(moved_center) if moved_center[0] is None: moved_center[0] = _center[0] if moved_center[1] is None: moved_center[1] = _center[1] self.center = moved_center # Real collision code starts if hasattr(another_rect, "bbox"): tmp_rect = another_rect.bbox else: tmp_rect = another_rect does_collide = self.bbox.colliderect(tmp_rect) # Ends self.center = _center if does_collide: if from_container_obj: to_return = another_rect.container_obj else: to_return = another_rect else: to_return = False return to_return def collide_list_rect(self, rect_sequence, moved_center=(None, None), from_container_obj=False): ''' Returns a list of rects, which collide; else empty list ''' return_list = [] for each_rect in rect_sequence: if self.collide_rect(each_rect, moved_center, from_container_obj): return_list.append(each_rect) return return_list def collide_list_all_rect(self, rect_sequence, moved_center=(None, None), from_container_obj=False): ''' Returns True if all rects in rect_sequence which collide, else False ''' for each_rect in rect_sequence: if not self.collide_rect(each_rect, moved_center, from_container_obj): return False else: return True def collide_list_any_rect(self, rect_sequence, moved_center=(None, None), from_container_obj=False): '''Returns the first rect which collides, else False''' for each_rect in rect_sequence: if self.collide_rect(each_rect, moved_center, from_container_obj): return each_rect else: return False def collide_circle(self, another_circle, moved_center=(None, None), from_container_obj=False): if not (len(moved_center) == 2): print "moved_center must be of the form (x,y)" _center = self.center moved_center = list(moved_center) if moved_center[0] is None: moved_center[0] = _center[0] if moved_center[1] is None: moved_center[1] = _center[1] self.center = moved_center # Collision code (uses Circles collision method) starts does_collide = another_circle.collide_rect(self) # Ends self.center = _center if does_collide: if from_container_obj: to_return = another_circle.container_obj else: to_return = another_circle else: to_return = False return to_return def collide_list_circle(self, circle_sequence, moved_center=(None, None), from_container_obj=False): ''' Returns a list of Circles, which collide; else empty list ''' return_list = [] for each_circle in circle_sequence: if self.collide_circle(each_circle, moved_center, from_container_obj): return_list.append(each_circle) return return_list def collide_list_all_circle(self, circle_sequence, moved_center=(None, None), from_container_obj=False): ''' Returns True if all circles in circle_sequence which collide, else False ''' for each_circle in circle_sequence: if not self.collide_circle(each_circle, moved_center, from_container_obj): return False else: return True def collide_list_any_circle(self, circle_sequence, moved_center=(None, None), from_container_obj=False): '''Returns the first circle which collides, else False''' for each_circle in circle_sequence: if self.collide_circle(each_circle, moved_center, from_container_obj): return each_circle else: return False def collide_img(self, another_img, moved_center=(None, None), from_container_obj=False): ''' Checks if this rect intersects with another_img. ''' does_collide = self.collide( another_img.col_shape, moved_center, from_container_obj=False) if does_collide: if from_container_obj: to_return = another_img.container_obj else: to_return = another_img else: to_return = False return to_return def collide_list_img(self, list_img, moved_center=(None, None), from_container_obj=False): list_collided_img = [] for each_img in list_img: if self.collide_img(each_img, moved_center, from_container_obj): # Collide_img itself takes container_obj into account, so no # need of post-processing list_collided_img.append(each_img) else: # else of the for loop return False return list_collided_img