diff --git a/0.py b/0.py new file mode 100644 index 0000000..9e70598 --- /dev/null +++ b/0.py @@ -0,0 +1,14 @@ +class A(object): + + """docstring for A""" + a = 2 + pass + + +class B(object): + + """docstring for A""" + pass + b = A.a + +print("Run completed") \ No newline at end of file diff --git a/Circle.py b/Circle.py new file mode 100644 index 0000000..be8a8b7 --- /dev/null +++ b/Circle.py @@ -0,0 +1,443 @@ +import pygame +from math_utils import distance +import math +import base +import inspect +import sprite_types +from utils import remove_from_list, rect_to_pygame_rect + + +class Circle(object): + map_class = "Circle" + + def __init__(self, center=(None, None), radius=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 (radius is None): + raise AttributeError("radius must be provided") + self.radius = radius + 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 + + @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, radius, color=None): + return cls(radius=radius, 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): + # left = self.center_x - self.radius + # top = self.center_y - self.radius + width = height = self.radius * 2 + return sprite_types.Rect(self.center, (width, height)) + # todo: remember about the colour. document - center is list unlike tuple + # in case of rect. + + def __repr__(self): + return "{0.__class__}( center={0.center}, radius={0.radius}, color={0.color})".format(self) + + def __copy__(self): + return self.__class__(self.center, self.radius, self.color) + + def move(self, move_x, move_y): + return Circle((self.center_x + move_x, self.center_y + move_y), self.radius, self.color) + + def move_ip(self, move_x, move_y): + self.center_x += move_x + self.center_y += move_y + return self + + def inflate(self, inflate_radius): + # check if resultant radius is -ve + return Circle(self.center, self.radius + inflate_radius, self.color) + + def inflate_ip(self, inflate_radius): + self.radius += inflate_radius + return self + + def is_inside(self, another_circle): + ''' Checks if this circle is fully inside another_circle ''' + if self.radius >= another_circle.radius: + return None + else: + bigger_circle = another_circle + smaller_circle = self + return distance(bigger_circle.center, smaller_circle.center) + smaller_circle.radius < bigger_circle.radius + + def is_inside_mutual(self, another_circle): + ''' Checks if this circle is fully inside another_circle or the opposite.''' + radius_diff = self.radius - another_circle.radius + if radius_diff > 0: + bigger_circle = self + smaller_circle = another_circle + elif radius_diff == 0: + # None of them can be inside the other + return None + else: + smaller_circle = self + bigger_circle = another_circle + return distance(bigger_circle.center, smaller_circle.center) + smaller_circle.radius < bigger_circle.radius + + 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, Circle): + return self.collide_circle(other_obj, moved_center, from_container_obj) + elif isinstance(other_obj, sprite_types.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, Circle): + return self.collide_list_circle(other_obj.list_instance, moved_center, from_container_obj) + elif issubclass(other_obj, sprite_types.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, 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 + + def collide_img(self, another_img, moved_center=(None, None), from_container_obj=False): + ''' Checks if this circle 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 + + def collide_circle(self, another_circle, moved_center=(None, None), from_container_obj=False): + ''' Checks if this circle intersects with another_circle. ''' + if not (len(moved_center) == 2): + raise AttributeError("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] + # temporarily move self to moved_center and shift back later + self.center = moved_center + does_collide = (distance(self.center, another_circle.center) <= ( + self.radius + another_circle.radius)) + 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_point(self, (point_x, point_y)): + ''' Checks if (point_x, point_y) is inside this circle. ''' + does_collide = ( + distance(self.center, (point_x, point_y)) <= self.radius) + if does_collide: + return True + else: + return False + + def collide_list_circle(self, list_circle, moved_center=(None, None), from_container_obj=False): + ''' Returns list of circles in list_circle which intersects with this circle. ''' + list_collided_circle = [] + for circle in list_circle: + if self.collide_circle(circle, moved_center, from_container_obj): + # Collide_circle itself takes container_obj into account, so no + # need of post-processing + list_collided_circle.append(circle) + else: # else of the for loop + return False + return list_collided_circle + + def collide_list_any_circle(self, list_circle, moved_center=(None, None), from_container_obj=False): + ''' Checks if this circle intersects with any of the circles in circle_list. ''' + for circle in list_circle: + if self.collide_circle(circle, moved_center, from_container_obj): + return True + else: # else of the for loop + return False + + def collide_list_all_circle(self, list_circle, moved_center=(None, None), from_container_obj=False): + ''' Checks if this circle intersects with all of the circles in circle_list. ''' + for circle in list_circle: + if not self.collide_circle(circle, moved_center, from_container_obj): + return False + else: # else of the for loop + return True + + def collide_rect(self, another_rect, moved_center=(None, None), from_container_obj=False): + ''' Checks if this circle collides with another_rect. ''' + 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 + does_collide = not ((self.center_x + self.radius) < another_rect.left or (self.center_x - self.radius) > another_rect.right or ( + self.center_y + self.radius) < another_rect.top or (self.center_y - self.radius) > another_rect.bottom) + 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, list_rect, moved_center=(None, None), from_container_obj=False): + ''' Returns list of rect in list_rect which intersects with this circle. ''' + list_collided_rect = [] + for rect in list_rect: + if self.collide_rect(rect, moved_center, from_container_obj): + list_collided_rect.append(rect) + else: # else of the for loop + return False + return list_collided_rect + + def collide_list_any_rect(self, list_rect, moved_center=(None, None), from_container_obj=False): + ''' Returns True if collides with any of the rect in the list. ''' + for temp_rect in list_rect: + if self.collide_rect(self, temp_rect, moved_center, from_container_obj): + return True + else: + return False + + def collide_list_all_rect(self, list_rect, moved_center=(None, None), from_container_obj=False): + ''' Returns True if collides with all the rect in the list. ''' + for temp_rect in list_rect: + if not self.collide_rect(self, temp_rect, moved_center, from_container_obj): + return False + else: + return True + + @classmethod + def get_circumcircle_from_rect(cls, another_rect): + # create a circle with same center and radius=sqrt(w^2+h^2)/2 + return cls(another_rect.center, math.sqrt(another_rect.width ** 2 + another_rect.height ** 2) / 2) + + @classmethod + def get_incircle_from_rect(cls, another_rect): + # radius of incircle is same as the (min of width and height)/2 (as it's not a + # square) + return cls(another_rect.center, min(another_rect.width, another_rect.height) / 2) + + # union -needs to be a circle *within* which the union of the bounding box of all the circles can be fit. + # warning about all the union functions: The circles returned are not + # necessarily the most optimum union circle, but it will surely contain + # the others. + def _union(self, circle_1, circle_2): + ''' ??? self.__union((new_center_x,new_center_y),new_radius) -> ([x,y],radius) which are the resultant values. ''' + return self.circumcircle_from_rect(circle_1.bbox.union(circle_2.bbox)) + + def union(self, new_circle): + ''' Returns a new circle which is the union of these two circles ''' + return self._union(self, new_circle) + + def union_ip(self, new_circle): + ''' Turns this circle into a circle which is a union of these two circles. ''' + temp_circle = self._union(self, new_circle) + self.center, self.radius = temp_circle.center, temp_circle.radius + return self + + # list_circle_params : [((x1,y1),radius_1), ((x2,y2),radius_2), ...] + def _unionall(self, circle_1, seq_circle): + ''' returns ([x,y],radius) for the resultant union of all circles. + sequence_circle must be a sequence of circles. ''' + return self.circumcircle_from_rect(circle_1.bbox.unionall([temp_circle.bbox for temp_circle in seq_circle])) + + def unionall(self, seq_circle): # seq_circle: sequence of circles + return self._unionall(self, seq_circle) + + def unionall_ip(self, seq_circle): + ''' Turns this circle into a circle which is a union of this circles with all circles in seq_circle''' + temp_circle = self._unionall(self, seq_circle) + self.center, self.radius = temp_circle.center, temp_circle.radius + return self + + # draw functions + def blit(self, surface, (x, y)=(None, None), color=None, clip_rect=None, width=1, use_antialiasing=False): + ''' Draws this Circle on surface. It must be of width and height greater than or equal to its bbox. + (x,y)->co-ordinates whether the center of the Circle should be drawn on the surface. + width argument doesnt work with antialiasing. width=0 makes it filled. + antialiasing may break in later versions as the backend pygame.gfxdraw is experimental. + color falls back first to Circle.color (if present) and then to blue color.''' + 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.circle(surface, color, (x, y), self.radius, width) + else: + pygame.gfxdraw.aacircle(surface, x, y, self.radius, color) + + 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": "Circle", + # Very IMP: If using Rect for bbox, use rect_to_pygame_rect like + # below. + "bbox": rect_to_pygame_rect(self.bbox), + "config": { + "center": self.center, + "radius": self.radius, + "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) + + def get_surface(self, surface=None, flags=0): + ''' Returns a new surface with dimensions of (Circle.bbox.width,Circle.bbox.width) and other properties as surface. + This Circle can be safely drawn on it.''' + if surface: + return pygame.Surface((self.bbox.width, self.bbox.height), flags, surface) + else: + return pygame.Surface((self.bbox.width, self.bbox.height), flags) + + def get_surface_drawn(self, (x, y)=(None, None), color=None, width=1, use_antialiasing=False, surface=None, flags=0): + ''' Returns a surface which has the tiltedRect drwan on it. ''' + temp_surf = self.get_surface(surface, flags) + self.draw(temp_surf, (x, y), color, width, use_antialiasing) + return temp_surf + + # end of Circle class diff --git a/Circle.pyc b/Circle.pyc new file mode 100644 index 0000000..88b062d Binary files /dev/null and b/Circle.pyc differ diff --git a/Img.py b/Img.py new file mode 100644 index 0000000..997eb73 --- /dev/null +++ b/Img.py @@ -0,0 +1,422 @@ +import pygame +import sprite_types +import inspect +import base +from utils import remove_from_list + + +class Img(object): + + map_class = "Img" + + def __init__(self, center=(None, None), file_name_or_obj=None, namehint="", collision_shape=None, container_obj=None): + + if not (len(center) == 2): + raise TypeError("Center must be (x,y)") + x = center[0] + y = center[1] + if not container_obj: + if (x is None) or (y is None): + raise TypeError("Center=(x,y) must be provided") + else: + self.container_obj = None + self.center = (x, y) + else: + self.container_obj = container_obj # Use weakrefs + + if (file_name_or_obj is None): + raise TypeError( + "file_name_or_obj must be filename, file_obj or surface") + + if not isinstance(file_name_or_obj, pygame.Surface): + if namehint == "": + self.surf = pygame.image.load(file_name_or_obj) + else: + self.surf = pygame.image.load(file_name_or_obj, namehint) + else: + # From surface + # Todo: Some param for caching + self._surf = file_name_or_obj + + if collision_shape: + self._col_shape = collision_shape + else: + self._col_shape = self.bbox + + @property + def col_shape(self): + return self._col_shape + + @col_shape.setter + def col_shape(self, value): + self._col_shape = value + + def sync_col_shape(self): + self.col_shape.center = self.center + + @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 + self.sync_col_shape() + + @classmethod + def from_container_obj(cls, container_obj, file_name_or_obj, namehint=""): + return cls(file_name_or_obj=file_name_or_obj, namehint=namehint, container_obj=container_obj) + + @classmethod + def from_surface(cls, center, surf): + return cls(center=center, file_name_or_obj=surf) + + @property + def surf(self): + return self._surf + + @surf.setter + def surf(self, value): + if isinstance(value, pygame.Surface): + self._surf = value + else: + return TypeError("surf can only be set to anther pygame.Surface") + + @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): + '''Returns sprite_types.Rect copy of bounding box''' + surf_rect = self.get_rect() + return sprite_types.Rect.from_rect(surf_rect) + + def get_rect(self): + '''Returns pygame.Rect copy of bounding box''' + return self.surf.get_rect(center=self.center) + + def get_surface(self): + ''' Returns a copy of internal surface ''' + return self.surf.copy() + + def __repr__(self): + return "{0.__class__}( center={0.center}, size={0.size})".format(self) + + def __copy__(self): + return self.__cls__.from_surface(self.center, self.surf.copy()) + + def save_to_disk(self, filename): + pygame.image.save(self.get_surface(), filename) + return True + + @property + def size(self): + return self.surf.get_size() + + @size.setter + def size(self, value): + raise NotImplementedError( + "Setting size is ambiguous, so not implemented") + + @property + def width(self): + return self.get_width() + + @width.setter + def width(self, value): + raise NotImplementedError( + "Setting width is ambiguous, so not implemented") + + @property + def height(self): + return self.get_height() + + @height.setter + def height(self, value): + raise NotImplementedError( + "Setting height is ambiguous, so not implemented") + + @property + def w(self): + return self.width + + @w.setter + def w(self, value): + self.width = value + + @property + def h(self): + return self.height + + @h.setter + def h(self, value): + self.height = value + + # Surface methods not provided, apply on self.surf + def blit(self, surface, (x, y)=(None, None), clip_rect=None): + + 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 clip_rect: + # Set clip region + surface.set_clip(clip_rect) + + surface.blit(self.surf, self.bbox.topleft) + + 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": "Img", + "bbox": self.get_rect(), + "config": { + "center": self.center, + "file_name_or_obj": self.surf + } + } + 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''' + + 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, sprite_types.Rect): + return self.collide_rect(other_obj, moved_center, from_container_obj) + elif isinstance(other_obj, Img): + # Todo: add img collision + 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, sprite_types.Rect): + return self.collide_list_rect(other_obj.list_instance, moved_center, from_container_obj) + elif issubclass(other_obj, Img): + # Todo: add img collision + 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, 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 + + def collide_img(self, another_img, moved_center=(None, None), from_container_obj=False): + ''' Checks if this img 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 + + def collide_circle(self, another_circle, moved_center=(None, None), from_container_obj=False): + ''' Checks if this circle intersects with another_circle. ''' + if not (len(moved_center) == 2): + raise AttributeError("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] + # temporarily move self to moved_center and shift back later + self.center = moved_center + does_collide = another_circle.collide_img(self) + 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_point(self, (point_x, point_y)): + ''' Checks if (point_x, point_y) is inside this circle. ''' + does_collide = self.col_shape.collide_point((point_x, point_y)) + if does_collide: + return True + else: + return False + + def collide_list_circle(self, list_circle, moved_center=(None, None), from_container_obj=False): + ''' Returns list of circles in list_circle which intersects with this circle. ''' + list_collided_circle = [] + for circle in list_circle: + if self.collide_circle(circle, moved_center, from_container_obj): + # Collide_circle itself takes container_obj into account, so no + # need of post-processing + list_collided_circle.append(circle) + else: # else of the for loop + return False + return list_collided_circle + + def collide_list_any_circle(self, list_circle, moved_center=(None, None), from_container_obj=False): + ''' Checks if this circle intersects with any of the circles in circle_list. ''' + for circle in list_circle: + if self.collide_circle(circle, moved_center, from_container_obj): + return True + else: # else of the for loop + return False + + def collide_list_all_circle(self, list_circle, moved_center=(None, None), from_container_obj=False): + ''' Checks if this circle intersects with all of the circles in circle_list. ''' + for circle in list_circle: + if not self.collide_circle(circle, moved_center, from_container_obj): + return False + else: # else of the for loop + return True + + def collide_rect(self, another_rect, moved_center=(None, None), from_container_obj=False): + ''' Checks if this circle collides with another_rect. ''' + 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 + does_collide = another_rect.collide_img(self) + 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, list_rect, moved_center=(None, None), from_container_obj=False): + ''' Returns list of rect in list_rect which intersects with this circle. ''' + list_collided_rect = [] + for rect in list_rect: + if self.collide_rect(rect, moved_center, from_container_obj): + list_collided_rect.append(rect) + else: # else of the for loop + return False + return list_collided_rect + + def collide_list_any_rect(self, list_rect, moved_center=(None, None), from_container_obj=False): + ''' Returns True if collides with any of the rect in the list. ''' + for temp_rect in list_rect: + if self.collide_rect(self, temp_rect, moved_center, from_container_obj): + return True + else: + return False + + def collide_list_all_rect(self, list_rect, moved_center=(None, None), from_container_obj=False): + ''' Returns True if collides with all the rect in the list. ''' + for temp_rect in list_rect: + if not self.collide_rect(self, temp_rect, moved_center, from_container_obj): + return False + else: + return True diff --git a/Img.pyc b/Img.pyc new file mode 100644 index 0000000..2bac40b Binary files /dev/null and b/Img.pyc differ diff --git a/README.html b/README.html new file mode 100644 index 0000000..c6c16c6 --- /dev/null +++ b/README.html @@ -0,0 +1,258 @@ +README

pyFun

+

(Requires pygame)

+

pyFun is a just-started WIP python project aiming to mix the simplicity of Gamemaker with the productiveness of Python and Pygame.

+

It is similar to Stellar Game Engine, but I wish to make it better.

+

It is a easier-to-use framework on top of pygame which gives you the ease of Gamemaker, but the code is still python. So, you can use all the good things of the python language. The framework is totally configurable, so that individual parts of it can be turned off and you get ALL and I repeat this, ALL the flexibility of pygame.

+

You can see the future goals at Upcoming implementations.markdown. +It is a work-in-progress project, so contributers are always welcome.

\ No newline at end of file diff --git a/Rect.py b/Rect.py new file mode 100644 index 0000000..a982998 --- /dev/null +++ b/Rect.py @@ -0,0 +1,641 @@ +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 diff --git a/Rect.pyc b/Rect.pyc new file mode 100644 index 0000000..1e5cd9d Binary files /dev/null and b/Rect.pyc differ diff --git a/Rotated_Rect.py b/Rotated_Rect.py new file mode 100644 index 0000000..ab3c485 --- /dev/null +++ b/Rotated_Rect.py @@ -0,0 +1,298 @@ +import pygame +from math_utils import distance +import math +import base +from Circle import Circle + +# start of Rotated_Rect class + + +class Rotated_Rect(pygame.Rect): + # make a metaclass which wraps a lot of functions just like move is defined below. + # functions to be wrapped - move,inflate + # Think about them - clamp,clip,union,fit,contains,collide + # the metaclass can probably also change the __str__ of the class. + # About drawing - use pygame.transform.rotate to get a rotated surface to + # be drawed. + + def __init__(self, leftTop_or_center, w_h, angle, centered_coords=False, color=None): + ''' Angle in degrees. + leftTop_or_center, w_h -> both are 2-element tuples or list + centered_coords -> whether the (left,top) is actually intended to be (center_x,center_y) or not. + I.e. if centered_coords==True the (left,top) co-ordinates are used as (center_x,center_y) co-ordinates of the Rotated_Rect''' + self.angle = angle + self.color = color + try: + left = leftTop_or_center[0] + top = leftTop_or_center[1] + except: + raise AttributeError( + "leftTop_or_center must be of format (left,top)") + try: + width = w_h[0] + height = w_h[1] + except: + raise AttributeError("w_h must be of format (width,height)") + + if centered_coords: + left = left - width / 2 + top = top - height / 2 + super(Rotated_Rect, self).__init__((left, top), (width, height)) + + def __copy__(self): + return Rotated_Rect((self.left, self.top), (self.width, self.height), self.angle) + + def __repr__(self): + return "{0.__class__.__name__}((top={0.top}, left={0.left}), (width={0.width}, height={0.height}), angle={0.angle}, color={0.color})".format(self) + + @classmethod + def create_from_rect(cls, another_rect, angle): + return cls((another_rect.left, another_rect.top), (another_rect.width, another_rect.height), angle) + + def get_rect(self): + return pygame.Rect((self.left, self.top), (self.width, self.height)) + + # most in_place versions retained from super-class + + def move(self, x, y): + return self.__copy__().move_ip(x, y) + + def inflate(self, x, y): + return self.__copy__().inflate_ip(x, y) + + def normalize(self): + # not sure what this one does - ask others + return self.__copy__().normalize_ip() + + def rotate_ip(self, some_angle): + self.angle += some_angle + + def rotate(self, some_angle): + return self.__copy__().rotate_ip(some_angle) + + # generalise the parameters to accept x1,y1,x2,y2,rot_angle + def rotate_point_about_center(self, x, y, theta=None): + ''' x,y -> Absolute co-ords of the point to be rotated wrt to the rectangles center. + self.centerx,self.centery are internally substracted from them. + theta -> angle (in degrees) by which the point is to be rotated. Default value is self.angle. + Returns absolute positions of the points after rotation. ''' + if theta is None: + theta = self.angle + return self.rotate_point(x, y, self.centerx, self.centery, theta) + + @staticmethod + # to be allowed as rotate_point(class) + def rotate_point(x, y, origin_x, origin_y, theta): + ''' x,y -> Absolute co-ords of the point to be rotated. origin_x,origin_y are internally substracted from them. + (origin_x,origin_y) -> Absolute co-ords of the origin wrt to which the point will be rotated + theta -> angle (in degrees) by which the point is to be rotated. + Returns absolute positions of the points after rotation. ''' + import math + x_diff = x - origin_x + y_diff = y - origin_y + # -y_diff because of the inverted nature of y co-ordinate system in pygame + y_diff = -y_diff + r = math.sqrt(x_diff ** 2 + y_diff ** 2) + initial_angle = math.atan2(y_diff, x_diff) + final_angle = initial_angle + math.radians(theta) + rotated_x_diff = r * math.cos(final_angle) + rotated_y_diff = r * math.sin(final_angle) + rotated_y_diff = -rotated_y_diff # - used for same reason as above + rotated_x = rotated_x_diff + origin_x + rotated_y = rotated_y_diff + origin_y + return (rotated_x, rotated_y) + + def rotate_point_polar(self, r, initial_angle, theta=None): + ''' theta and initial_angle in radians. + initial_angle ->angle r makes with horizontal. + theta -> angle to be rotated by. ''' + if theta is None: + theta = self.angle + final_angle = initial_angle + theta + rotated_x = r * math.cos(final_angle) + rotated_y = r * math.sin(final_angle) + return (rotated_x, rotated_y) + + @property + def bottomleft_rotated(self): + temp = self.bottomleft + return self.rotate_point_about_center(temp[0], temp[1]) + + @property + def topleft_rotated(self): + temp = self.topleft + return self.rotate_point_about_center(temp[0], temp[1]) + + @property + def bottomright_rotated(self): + temp = self.bottomright + return self.rotate_point_about_center(temp[0], temp[1]) + + @property + def topright_rotated(self): + temp = self.topright + return self.rotate_point_about_center(temp[0], temp[1]) + + @property + def midleft_rotated(self): + temp = self.midleft + return self.rotate_point_about_center(temp[0], temp[1]) + + @property + def midright_rotated(self): + temp = self.midright + return self.rotate_point_about_center(temp[0], temp[1]) + + @property + def midtop_rotated(self): + temp = self.midtop + return self.rotate_point_about_center(temp[0], temp[1]) + + @property + def midbottom_rotated(self): + temp = self.midbottom + return self.rotate_point_about_center(temp[0], temp[1]) + + # related to bounding box + @property + def bbox(self): + list_x = [self.topleft_rotated[0], self.topright_rotated[0], + self.bottomleft_rotated[0], self.bottomright_rotated[0]] + list_y = [self.topleft_rotated[1], self.topright_rotated[1], + self.bottomleft_rotated[1], self.bottomright_rotated[1]] + min_x = math.floor(min(list_x)) + max_x = math.ceil(max(list_x)) + min_y = math.floor(min(list_y)) + max_y = math.ceil(max(list_y)) + return pygame.Rect((min_x, min_y), (max_x - min_x, max_y - min_y)) + # corners + + @property + def corners(self): + ''' List of actual (rotated) corners. ''' + return [self.topleft_rotated, self.topright_rotated, self.bottomright_rotated, self.bottomleft_rotated] + + @property + def corners_relative(self): + return [(temp[0] - self.centerx, temp[1] - self.centery) for temp in self.corners] + + # circles related to the rect + def get_circumcircle(self): + Circle.get_circumcircle_from_rect(self.bbox) + + def get_incircle(self): + # get_rect used as incircle doesnt depend upon theta, same for rot and + # non-rot rect + Circle.get_incircle_from_rect(self.get_rect()) + + # collision events + def collide_point(self, *args): + ''' collidepoint(x,y) -> + collidepoint((x,y)) -> ''' + temp_len = len(args) + temp_x = None + temp_y = None + if temp_len == 2: + temp_x = args[0] + temp_y = args[1] + elif temp_len == 1: + if len(args[0]) == 2: + temp_x, temp_y = args[0] + else: + raise TypeError("Arguments are of incorrect type") + else: + raise TypeError("Arguments are of incorrect type") + if temp_x is None or temp_y is None: + raise TypeError("argument must contain two numbers") + return self.get_rect().collidepoint(self.rotate_point_about_center(temp_x, temp_y, -self.angle)) + + def collide_rect(self, rect): + ''' Tests if it collides with rect, which should be instance of rect class. ''' + # First test:whether bbox collides with rect. If not, no collision. + if not self.bbox.colliderect(rect): + return False + else: + # Todo: Else sure test: Need to do line class first. Test if any line + # of titedRect collides with any line of (instead, the whole) rect. + pass + + # draw functions + def blit(self, surface, (x, y)=(None, None), color=None, clip_rect=None, width=1, use_antialiasing=False, blend=True): + ''' Draws this Circle on surface. It must be of width and height greater than or equal to its bbox. + (x,y)->co-ordinates whether the center of the Rotated_Rect should be drawn on the surface. + width argument doesnt work with antialiasing. width=0 makes it filled. + antialiasing may break in later versions as the backend pygame.gfxdraw is experimental. + color falls back first to Circle.color (if present) and then to blue color.''' + if not ((x, y) == (None, None)): + sprite_to_draw = self.__copy__() + sprite_to_draw.center = (x, y) + else: + sprite_to_draw = self + # 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) + + iter_corners = [(temp[0] + x, temp[1] + y) + for temp in sprite_to_draw.corners_relative] + if not use_antialiasing: + pygame.draw.polygon(surface, color, iter_corners, width) + else: + pygame.draw.aalines(surface, color, True, iter_corners, blend) + + if clip_rect: + # Remove clip region + surface.set_clip(None) + + def draw(self): + tmp_dirty_sprite = { + "sprite": self, + "shape": "Rotated_Rect", + "bbox": self.bbox, + "config": { + "leftTop_or_center": (self.left, self.top), + "w_h": (self.w, self.h), + "angle": self.angle, + "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 + 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) + + def get_surface(self, surface=None, flags=0): + ''' Returns a new surface with dimensions of (Rotated_Rect.bbox.width,Rotated_Rect.bbox.width) and other properties as surface. + This Rotated_Rect can be safely drawn on it.''' + if surface: + return pygame.Surface((self.bbox.width, self.bbox.height), flags, surface) + else: + return pygame.Surface((self.bbox.width, self.bbox.height), flags) + + def get_surface_drawn(self, (x, y)=(None, None), color=None, width=1, use_antialiasing=False, blend=True, surface=None, flags=0): + ''' Returns a surface which has the Rotated_Rect drwan on it. ''' + temp_surf = self.get_surface(surface, flags) + self.draw(temp_surf, (x, y), color, width, use_antialiasing, blend) + return temp_surf + + def collide_Rotated_Rect(self, another_Rotated_Rect): + '''Checks whether this Rotated_Rect collides with another_titltedrect, which must be a instance of titledRect. ''' + # Rotates both Rotated_Rect by -another_Rotated_Rect.angle. So, another_Rotated_Rect becomes a Rect called temp_Rect. + # Then check if the rotated version of this Rotated_Rect + # (temp_Rotated_Rect) collides with the obtained Rect. + temp_Rotated_Rect = self.rotate(-another_Rotated_Rect.angle) + temp_Rect = another_Rotated_Rect.get_rect() + return temp_Rotated_Rect.colliderect(temp_Rect) diff --git a/UncasedDict.py b/UncasedDict.py new file mode 100644 index 0000000..17c65b9 --- /dev/null +++ b/UncasedDict.py @@ -0,0 +1,21 @@ +class UncasedDict(dict): + + def __getitem__(self, key): + for each_key in self: + if each_key.upper() == key.upper(): + return super(UncasedDict, self).__getitem__(each_key) + else: + return super(UncasedDict, self).__getitem__(key) + + def has_key(self, key): + return self.__contains__(key) + + def get(self, key): + return self.__getitem__(key) + + def __contains__(self, key): + for each_key in self: + if each_key.upper() == key.upper(): + return True + else: + return False diff --git a/UncasedDict.pyc b/UncasedDict.pyc new file mode 100644 index 0000000..ce99098 Binary files /dev/null and b/UncasedDict.pyc differ diff --git a/awesome.py b/awesome.py new file mode 100644 index 0000000..b84b5ee --- /dev/null +++ b/awesome.py @@ -0,0 +1,40 @@ +# Using metaclass and decorator to allow class access during class creation time +from functools import partial +import inspect + +class meta(type): + def __new__(cls, name, base, clsdict): + temp_cls = type.__new__(cls, name, base, clsdict) + methods=inspect.getmembers(temp_cls,inspect.ismethod) + for (method_name,method_obj) in methods: + tmp_spec=inspect.getargspec(method_obj) + if "main_func" in tmp_spec.args: + what_to_do,main_func=tmp_spec.defaults + f=method_obj.im_func + f.func_code,f.func_defaults,f.func_dict,f.func_doc,f.func_name=main_func.func_code, main_func.func_defaults,main_func.func_dict,main_func.func_doc,main_func.func_name + what_to_do(method_obj) + + return temp_cls + +def do_it(what_to_do, main_func=None): + if main_func is None: + return partial(do_it,what_to_do) + def whatever(what_to_do=what_to_do,main_func=main_func): + pass + return whatever + +def original_decorator(func): + func.im_class.a.append("so cool") + print "Calling original decorator" + + +class A(object): + __metaclass__=meta + a=[] + @do_it(original_decorator) + def some_method(self,a=5): + ''' hello''' + print "Calling original method" + +print("A.a is %s"%A.a) +print(inspect.getargspec(A().some_method)) \ No newline at end of file diff --git a/awesome_better.py b/awesome_better.py new file mode 100644 index 0000000..b533ad8 --- /dev/null +++ b/awesome_better.py @@ -0,0 +1,110 @@ + +from functools import partial +import inspect + +def sync_func(func1, func2): + '''Makes the 1st param (func) equivalent to 2nd param (func).''' + func1.func_code = func2.func_code + func1.func_defaults = func2.func_defaults + func1.func_dict = func2.func_dict + func1.func_doc = func2.func_doc + func1.func_name = func2.func_name + + +class meta(type): + # Using metaclass and decorator to allow class access during class creation time + # No method defined within the class should have "_process_meta" as arg + # Potential problems: Using closures, function.func_globals is read-only + # Doesnt get appropiate globals, maybe pass it (or the reqd global vars) as param on func defn + def __new__(cls, name, base, clsdict): + temp_cls = type.__new__(cls, name, base, clsdict) + methods = inspect.getmembers(temp_cls, inspect.ismethod) + for (method_name, method_obj) in methods: + tmp_spec = inspect.getargspec(method_obj) + if "__process_meta" in tmp_spec.args: + what_to_do, main_func = tmp_spec.defaults[:-1] + f = method_obj.im_func + sync_func(f, main_func) + mod_func = what_to_do(temp_cls, f) + sync_func(f, mod_func) + + return temp_cls + + +def do_it(what_to_do, main_func=None): + if main_func is None: + return partial(do_it, what_to_do) + + def whatever(what_to_do=what_to_do, main_func=main_func, __process_meta=True): + pass + return whatever +def run_it(run_func): + def what_to_do(cls, func): + func(cls) + return func + return do_it(what_to_do=what_to_do,main_func=run_func) + + +def original_classmethod_decorator(cls, func): + # append default arg values to class variable "a" + func_defaults = inspect.getargspec(func).defaults + cls.a.append(func_defaults) + func.__doc__ = "This is a class method" + print "Calling original classmethod decorator" + return func + + +def original_method_decorator(cls, func): + # append default arg values to class variable "a" + func_defaults = inspect.getargspec(func).defaults + cls.a.append(func_defaults) + func.__doc__ = "This is a instance method" # Can change func properties + print "Calling original method decorator" + return func + + +def decorator_to_classmethod(cls, func): + cls.original_classmethod_decorator = classmethod( + original_classmethod_decorator) + return func + +b=[] + +class A(object): + __metaclass__ = meta + a = [] +## original_classmethod_decorator = classmethod( +## original_classmethod_decorator) +## original_method_decorator = classmethod(original_method_decorator) + + @classmethod + @do_it(original_classmethod_decorator) + def some_method(cls, x=1): + ''' hello''' + print "Calling original class method" + + @classmethod + @run_it + def register_class(cls): + b.append(cls) + + +## +# @classmethod +# @do_it(decorator_to_classmethod) +# def original_classmethod_decorator(cls): +# pass + +# @classmethod +# @do_it(decorator_to_classmethod) +# def original_method_decorator(cls): +# pass + + @do_it(original_method_decorator) + def some_method_2(self, y=2): + ''' hello again''' + print "Calling original method" + +# signature preserved +print(inspect.getargspec(A.some_method)) +print(inspect.getargspec(A.some_method_2)) diff --git a/awesome_try.py b/awesome_try.py new file mode 100644 index 0000000..65571b9 --- /dev/null +++ b/awesome_try.py @@ -0,0 +1,81 @@ +# Using metaclass and decorator to allow class access during class creation time +# No method defined within the class should have "_process_meta" as arg +# Potential problems: Using closures, function.func_globals is read-only +# => may cause problem +from functools import partial +import inspect + + +def sync_func(func1, func2): + '''Makes the 1st param (func) equivalent to 2nd param (func).''' + func1.func_code = func2.func_code + func1.func_defaults = func2.func_defaults + func1.func_dict = func2.func_dict + func1.func_doc = func2.func_doc + func1.func_name = func2.func_name + + +class meta(type): + + def __new__(cls, name, base, clsdict): + temp_cls = type.__new__(cls, name, base, clsdict) + methods = inspect.getmembers(temp_cls, inspect.ismethod) + for (method_name, method_obj) in methods: + tmp_spec = inspect.getargspec(method_obj) + if "__process_meta" in tmp_spec.args: + what_to_do, main_func = tmp_spec.defaults[:-1] + # is_classmethod=False + # if method_obj.__self__==temp_cls: + # is_classmethod=True + f = method_obj.im_func + sync_func(f, main_func) + mod_func = what_to_do(temp_cls, f) + sync_func(f, mod_func) + + return temp_cls + + +def do_it(what_to_do, main_func=None): + if main_func is None: + return partial(do_it, what_to_do) + + def whatever(what_to_do=what_to_do, main_func=main_func, __process_meta=True): + pass + return whatever + + +def original_classmethod_decorator(cls, func): + # append default arg values to class variable "a" + func_defaults = inspect.getargspec(func).defaults + cls.a.append(func_defaults) + func.__doc__ = "This is a class method" + print "Calling original classmethod decorator" + return func + + +def original_method_decorator(cls, func): + # append default arg values to class variable "a" + func_defaults = inspect.getargspec(func).defaults + cls.a.append(func_defaults) + func.__doc__ = "This is a instance method" + print "Calling original method decorator" + return func + + +class A(object): + __metaclass__ = meta + a = [] + + @classmethod + @do_it(original_classmethod_decorator) + def some_method(cls, x=1): + ''' hello''' + print "Calling original class method" + + @do_it(original_method_decorator) + def some_method_2(self, y=2): + ''' hello again''' + print "Calling original method" + +print(inspect.getargspec(A.some_method)) +print(inspect.getargspec(A.some_method_2)) diff --git a/base.py b/base.py index f0fa198..0ed0878 100644 --- a/base.py +++ b/base.py @@ -1,785 +1,450 @@ -# only for python 2.x -from uniquelist import * # imports uniquelist +# only for python 2.x +# VERY IMP: Use weakrefs instead of list for fun_Game.list_class,\ +# fun_Class.list_instance and fun_Class.list_update_func + +from uniquelist import uniquelist # imports uniquelist import pygame -from pygame.locals import * # imports the constants -from math import * -#Mouse constants -B_LEFT_CLICK=1 -B_RIGHT_CLICK=3 -B_MIDDLE_CLICK=2 -B_SCROLLUP=4 -B_SCROLLDOWN=5 +from pygame.locals import * # imports the constants +import math +from utils import register_class, meta_accessor_class, register_action +# import utils +import logging +from copy import deepcopy +from math_utils import atan2_inv, distance, vel_add, direction +import event + +logging.basicConfig(level=logging.DEBUG) + +IS_TEST_MODE = True + +# Mouse constants +B_LEFT_CLICK = 1 +B_RIGHT_CLICK = 3 +B_MIDDLE_CLICK = 2 +B_SCROLLUP = 4 +B_SCROLLDOWN = 5 # Direction constants -TOP=1 -LEFT=2 -BOTTOM=3 -RIGHT=4 +TOP = 1 +LEFT = 2 +BOTTOM = 3 +RIGHT = 4 + class fun_Game(object): - list_class=uniquelist() - list_event=[] # event_list of this step - list_event_old=[] # event_list of the previous step - list_state_all_buttons=[] # A sequence of boolean representing the state of every key - list_state_all_buttons_old=[] # A sequence of boolean representing the state of every key of previous step - def __init__(self,width,height,fps=30): + list_class = uniquelist() + + def __init__(self, width, height, fps=30): pygame.init() - self.fps=fps - self.clock=pygame.time.Clock() - self.width=width - self.height=height + self.fps = fps + self.clock = pygame.time.Clock() + self.width = width + self.height = height + self.test_init() - def update_all_class(self): - for temp_cls in fun_Game.list_class: - for temp_count in temp_cls.list_instance: - temp_count.update() + @property + def w(self): + return self.width - def _cache_event_list(self): - ''' Cache event_list in every step. - All events in fun_Game.list_event have a "type" property which are the usual constants. ''' - fun_Game.list_event_old=fun_Game.list_event # move current list to old list - fun_Game.list_state_all_buttons_old=fun_Game.list_state_all_buttons + @w.setter + def w(self, val): + self.width = val - fun_Game.list_event=pygame.event.get() - fun_Game.list_state_all_buttons=pygame.key.get_pressed() #Returns a sequence of boolean representing the state of every key. Use key constant values for indexing. - #print fun_Game.list_event # debug message - # control access to list_event - # with getter, do something crazy for filtering - # or a filtering function + @property + def h(self): + return self.height + + @h.setter + def h(self, val): + self.height = val + +# @classmethod +# def _register_class_(cls, class_to_register): +# ''' Registers class_to_register to its list_class ''' +# cls.list_class.append(class_to_register) + @classmethod + def register_class(cls, cls_to_register): + cls.list_class.append(cls_to_register) + + dispatch_events_class = classmethod(event.dispatch_events_class) + + @classmethod + def for_all_instance(cls, func_to_run, flatten): + ''' Note: flatten can be "level-instance" or "level-class" or False''' + return_list = [] + for tmp_cls in cls.list_class: + return_item = tmp_cls.for_all_instance( + func_to_run, flatten=flatten) + if flatten == "level-instance" or flatten == "level-class": + return_list.extend(return_item) + else: + return_list.append(return_item) + return return_list + + @classmethod + def for_all_class(cls, func_to_run, flatten): + ''' Note: flatten can be True or False''' + return_list = [] + for tmp_class in cls.list_class: + return_item = func_to_run(tmp_class) + if flatten: + return_list.extend(return_item) + else: + return_list.append(return_item) + return return_list + + @staticmethod + def collect_self_clear_rect(self): + _list_clear_rect = [] + _list_rendered_sprite = [] + # perf hog: search for better way to remove list items iteratively + for tmp_old_sprite in self.list_rendered_sprite: + if (not tmp_old_sprite.get("still_valid")): + # returns bbox of all list_rendered_sprite that are not still_valid, after removing them from that list + # so, list now contains only of the older sprites still_valid + tmp_clear_rect_bbox = tmp_old_sprite["bbox"] + _list_clear_rect.append(tmp_clear_rect_bbox) + else: + _list_rendered_sprite.append(tmp_old_sprite) + if ("still_valid" in tmp_old_sprite): + tmp_old_sprite.pop("still_valid") + + # in next step : + # _list_rendered_sprite.append(self.list_dirty_sprite) + # self.list_dirty_sprite=[] + self.list_rendered_sprite = _list_rendered_sprite + return _list_clear_rect + + @staticmethod + def collect_self_rendered_sprite(self): + return self.list_rendered_sprite + + @staticmethod + def collect_self_dirty_sprite(self): + return self.list_dirty_sprite + + @staticmethod + def move_dirty_to_rendered(self): + self.list_rendered_sprite.extend(self.list_dirty_sprite) + self.list_dirty_sprite = [] + return None + + def blit_all_class(self): + # step 1: collect all clear rects + # i.e. remove last_rendered_rects which are not still_valid and return them + # also move to_be_rendered_sprites into that list and clear dirty list + list_clear_rect = self.for_all_instance( + self.collect_self_clear_rect, flatten="level-instance") + list_rendered_sprite = self.for_all_instance( + self.collect_self_rendered_sprite, flatten="level-instance") + list_dirty_sprite = self.for_all_instance( + self.collect_self_dirty_sprite, flatten="level-instance") + + # step 2: check if any clear_rect intersects with last_rendered_rects, + # return a list of dirty overlay for those last_rendered_rects. + # also if it is contained (totally within), mark clear_rect for removal + list_dirty_overlay_sprite = [] + list_cancelled_clear_rect = [] + for tmp_old_sprite in list_rendered_sprite: + for tmp_clear_rect in list_clear_rect: + tmp_intersect_rect = tmp_clear_rect.clip( + tmp_old_sprite["bbox"]) + if tmp_intersect_rect.w > 0 and tmp_intersect_rect.h > 0: + # make blit funcs capable of taking a clip rect, will be + # appplied before blitting + copy_old_sprite = deepcopy(tmp_old_sprite) + copy_old_sprite["clip_rect"] = tmp_intersect_rect + list_dirty_overlay_sprite.append(copy_old_sprite) + + if tmp_old_sprite["bbox"].contains(tmp_clear_rect): + # Cancel clear rect + list_cancelled_clear_rect.append(tmp_clear_rect) + + else: + # No explicit blit + pass + # step 3: for every clear_rect not marked for removal, do the + # clear_rects. +# print "Clear_rects= ",len(list_clear_rect)-len(list_cancelled_clear_rect) + # i = 0 + for tmp_clear_rect in list_clear_rect: + # convert to pygame rect + if not (tmp_clear_rect in list_cancelled_clear_rect): + self.screen.fill(pygame.Color(0, 0, 0, 1), tmp_clear_rect) + # i += 1 + # print "i=", i + + # step 4: do blit for all dirty_sprite and dirty_overlay + for tmp_dirty_sprite in list_dirty_sprite: + tmp_dirty_sprite["sprite"].blit(self.screen) + +# print "Dirty_overlays= ",len(list_dirty_overlay_sprite) +# print list_dirty_overlay_sprite + for tmp_overlay_sprite in list_dirty_overlay_sprite: + new_overlay_sprite = tmp_overlay_sprite[ + "sprite"].__class__(**tmp_overlay_sprite["config"]) + new_overlay_sprite.blit( + self.screen, clip_rect=tmp_overlay_sprite["clip_rect"]) + + # step 5: update display + list_display_update = [tmp_dirty_sprite["bbox"] + for tmp_dirty_sprite in list_dirty_sprite] + list_clear_rect + + pygame.display.update(list_display_update) + +# if not (list_dirty_sprite[0]["sprite"].bbox in list_display_update): +# print("problem") + + # step 6: cleaning jobs + self.for_all_instance(self.move_dirty_to_rendered, flatten=False) +# pygame.display.flip() + + def test_init(self): + ''' Empty function to be overloaded for extra init on test mode. ''' + pass + + def test_update(self): + ''' Empty function to be overloaded for extra draw on test mode. ''' + pass def run(self): - self.screen=pygame.display.set_mode([self.width,self.height]) + self.screen = pygame.display.set_mode([self.width, self.height]) + frame_no = 0 while True: - self._cache_event_list() # Todo: Add other update-related functions - self.update_all_class() - # VERY IMPORTANT: Do a spare first step so that every variable / list gets initialised well. Start instant creation and execution from second step. - # IMPORTANT: First do all fun_Game related stuff before dealing with game objects. + # Todo: Always exit on event_close : make it default behavior, + # although override-able + frame_no += 1 + # print frame_no + event.collect_events() + self.dispatch_events_class() + self.blit_all_class() + self.test_update() + # VERY IMPORTANT: Do a spare first step so that every variable / list gets initialized well. + # Start instant creation and execution from second step. + # IMPORTANT: First do all fun_Game related stuff before dealing + # with game objects. self.clock.tick(self.fps) - self.screen.fill([50,100,150]) # Todo: Remove this debug statement - pygame.display.flip() # debug statement - #print(event) - #print(fun_Game.list_event_old) - # Todo - Add crazy functions to allow other objects and instances finding like GM - #class fun_Game ends here + # Todo - Add crazy functions to allow other objects and instances finding + # like GM + # Todo - Easy way to take screenshot and save it as pic + # Todo: Game testing module. Lets you compare screenshots and compare values with pixelarray or surface.get_at + # Also allows accessing different config values. -class meta_fun_Class(type): - def __new__(cls,name,base,clsdict): - temp_class=type.__new__(cls,name,base,clsdict) - fun_Game.list_class.append(temp_class) - cls.priority_order=cls.depth=len(fun_Game.list_class) #by default, priority_order and depth depends on "when class was defined" - return temp_class + # class fun_Game ends here +# print(fun_Game) +meta_fun_Class = meta_accessor_class + + +@register_class(fun_Game) class fun_Class(object): - __metaclass__=meta_fun_Class - list_instance=uniquelist() - #list_update_functions stores the list of functions to call in the given order. - #It can be used for injecting custom user functions (remember order). - list_update_functions=uniquelist() #action_draw not added as it depends on depth - list_check_functions=uniquelist() # all the check functions added here. what they do: if event_: action_ + # think about this: priority_order and depth + # by default, depends on "when class was defined" + # cls.priority_order = cls.depth = len(fun_Game.list_class) + __metaclass__ = meta_fun_Class - def __init__(self,x,y,img=None): - self.x=0 - self.y=0 - self.img=img #img should be a fun_Image, but lets do that later - fun_Class.list_instance.append(self) - self.action_create() + # Below lines moved to meta_fun_Class + # list_instance = uniquelist() + # list_check_func = uniquelist() + # dict_action_func -> dict of action funcs, key=event_numb like KEYDOWN + # dict_action_func = {} - def update(self): - '''calls all functions in list_update_functions in the given order''' - temp=None - for temp in fun_Class.list_update_functions: - temp(self) + def __init__(self, x=None, y=None): + # use sprite as a general term, img->one kind of sprite, rect, circle + self.x_start = x + self.y_start = y + self.center = [x, y] + self.x_prev = x + self.y_prev = y + self.vel_x, self.vel_y, self.accln_x, self.accln_y = 0, 0, 0, 0 - def draw(self): - # do some blitting depending on self.img - pass + if (self.sprite_defaults and self.sprite_defaults.get("type")): + if (x is None) or (y is None): + raise AttributeError( + "fun_Class with sprite_defaults must have x,y") - def register(list_to_register=None): - ''' Used as decorator: Adds the function to list_to_register ''' - def temp_func(what_to_register): - list_to_register.append(what_to_register) - return what_to_register - return temp_func - - @register(list_update_functions) - def action_begin_step(self): - pass - @register(list_update_functions) - def action_step(self): - tmp=self.event_mouse_click() - if tmp: - print tmp - print(self.mouse_get_focused()) - @register(list_update_functions) - def action_end_step(self): - pass - # creation and destroy event - def action_create(self): - ''' Better to use this function than overloading __init__() ''' - pass - def action_destroy(self): - ''' Executed when the instance is destroyed ''' - pass - def destroy(self): - action_destroy(self) - - # checks for all standard events -- functions named like check_ - # what they do: if event_: action_ - # so lets define event_,action_,check_ for each - - # keyboard handling (later move to keyboard module) - - # occurs if the key was held down continously since the last step - def event_key_pressed(self,key_value):#check if it works - if get_event_list(list_to_get=fun_Game.list_state_all_buttons)[key_value] \ - and get_event_list(list_to_get=fun_Game.list_state_all_buttons_old)[key_value]\ - and (not get_event_list(event_types=KEYUP,further_check_variable_name="key",further_check_value=key_value))\ - and (not get_event_list(event_types=KEYUP,further_check_variable_name="key",further_check_value=key_value,list_to_get=fun_Game.list_event_old)): - return True - - def action_key_pressed(self,key_value): - pass - def check_key_pressed(self): - pass # Todo: think about how to do it - - # occurs if the key is held at the moment (live) - def event_key_pressed_live(self,key_value):#check if it works - ''' Returns True if the key (represented by key_code) is currently pressed. - Problem is that this function is live. Returns True if the key is pressed at that moment. - So, for example, two calls in two consecutive steps may return True although the key has been left in the meantime. ''' - temp_all_buttons=get_event_list(list_to_get=fun_Game.list_state_all_buttons) #Returns a sequence of boolean representing the state of every key. Use key constant values for indexing. - if temp_all_buttons[key_value]: # indexing by key_value - return True - def action_key_pressed_live(self,key_value): - pass - def check_key_pressed_live(self): - temp_all_buttons=get_event_list(list_to_get=fun_Game.list_state_all_buttons) #Returns a sequence of boolean representing the state of every key. Use key constant values for indexing. - for temp in range(len(temp_all_buttons)): # temp represents the key code in numbers as it is the index of list_state_all_buttons indirectly - if temp_all_buttons[temp]: - self.action_key_pressed_live(temp) #temp is the keycode - - # occurs if a key press occurs (i.e. pushed down) - # key repetations may occur as the list is cached per step and there might be multiple (same key)press per step. - def event_key_down(self,key=None): - if key == None: - return get_event_list(KEYDOWN) + self.sprite = self.sprite_defaults.get("type").from_container_obj( + container_obj=self, **self.sprite_defaults["params"]) + self.list_dirty_sprite = [] + # list_rendered_sprite => sprites that are effectively on the + # screen now + self.list_rendered_sprite = [] else: - return get_event_list(KEYDOWN,"key",key) - def action_key_down(self,key): - pass - def check_key_down(self): - for temp in get_event_list(KEYDOWN): - action_key_down(self,temp.key) + self.sprite = None + self.list_instance.append(self) + # think about self.action_create() + self.push_event_local(event.Event("Instance_create")) - - #probably wierd thing to do - define all <>_key_down as equivalent to <>_key_press - event_key_press = event_key_down - action_key_press = action_key_down - check_key_press = check_key_down - - # occurs if key is released - def event_key_up(self,key=None): - if key==None: - return get_event_list(KEYUP) - else: - return get_event_list(KEYUP,"key",key) - def action_key_up(self,key): - pass - def check_key_up(self): - for temp in get_event_list(KEYUP): - action_key_up(self,temp.key) - # Todo: Do other key and keyboard related functions (look at GM for related functions) - # Mouse handling (later move it to mouse module) - #Mouse constants (defined at top - move them to a constants module, which will be imported in the global namespace) - #mouse click - def event_mouse_click(self,button=None): - if button==None: - return get_event_list(event_types=MOUSEBUTTONDOWN) - else: - return get_event_list(event_types=MOUSEBUTTONDOWN,further_check_variable_name="button",further_check_value=button) - def action_mouse_click(self,button): - pass - def check_mouse_click(self): - list_temp=get_event_list(event_types=MOUSEBUTTONDOWN) - for temp in list_temp: - action_mouse_click(temp.button) - #mouse get pressed - def event_mouse_get_pressed(self,button=None): - ''' event_mouse_get_pressed() -> tuple looking like (1,0,0) i.e. ( left_click_state, middle_click_state, right_click_state ). - Use an index which is (corresponding_constant - 1). For eg event_mouse_get_pressed()[0] returns left click state. - event_mouse_get_pressed(button) -> Returns 0 or 1 depending on pressed or not. ''' - if button==None: - return pygame.mouse.get_pressed() - else: - return pygame.mouse.get_pressed()[button-1] # because the tuple returned has o-based indexing - # end of mouse handling - # other mouse-based functions - def mouse_get_focused(self): - return pygame.mouse.get_focused() - def mouse_set_visible(self,bool_value): - return pygame.mouse.set_visible(bool_value) - def mouse_get_rel(self): - ''' Returns relative movement of the mouse since the last call to this function. - mouse_get_rel() -> (x,y) ''' - return pygame.mouse.get_rel() - def mouse_set_cursor_image(self,image=None): - pass # todo: complete after the image part is done - # implement mouse_pos as variable @property - def mouse_pos(self): - ''' mouse_pos -> (x,y) - mouse_pos = [x,y] -> Sets cursor position ''' - return pygame.mouse.get_pos() - @mouse_pos.setter - def mouse_pos(self,val): - ''' val should be a list [x,y] ''' - if not isinstance(val,list): - raise TypeError("val should be a list [x,y]") - pygame.mouse.set_pos(val) + def center(self): + return self._center + + @center.setter + def center(self, val): + if not hasattr(self, "_center"): + self._center = [None, None] + self._center[0] = val[0] + self._center[1] = val[1] - # implement mouse_x as variable @property - def mouse_x(self): - ''' mouse_x -> x - mouse_x = some_x -> Sets cursor x position ''' - return pygame.mouse.get_pos()[0] # get_pos -> (x,y) - @mouse_x.setter - def mouse_x(self,x): - pygame.mouse.set_pos([x,mouse_y]) + def x(self): + return self.center[0] + + @x.setter + def x(self, val): + self.center[0] = val - # implement mouse_y as variable @property - def mouse_y(self): - ''' mouse_y -> y - mouse_y = some_y -> Sets cursor y position ''' - return pygame.mouse.get_pos()[1] # get_pos -> (x,y) - @mouse_y.setter - def mouse_y(self,y): - pygame.mouse.set_pos([mouse_x,y]) - # end of mouse-related function - #other various events + def y(self): + return self.center[1] - def event_intersect_room_boundary(self): # todo: test after image part is done - ''' Checks if the object (its image bounding box) is intersecting the room boundary, but not totally outside the room. - Returns direction constants like TOP, LEFT, BOTTOM, RIGHT. ''' - if (self.x+self.image.bbox.width/2)>fun_Game.room_width and not (self.x-self.image.bbox.width/2)>fun_Game.room_width: - return RIGHT # all constants defined at global level - elif (self.y+self.image.bbox.height/2)>fun_Game.room_height and not (self.y-self.image.bbox.height/2)>fun_Game.room_height: - return BOTTOM - elif (self.x-self.image.bbox.width/2)<0 and not (self.x+self.image.bbox.width/2)<0: - return LEFT - elif (self.y-self.image.bbox.height/2)<0 and not (self.y+self.image.bbox.height/2)<0: - return TOP - def action_intersect_room_boundary(self): + @y.setter + def y(self, val): + self.center[1] = val + + @property + def vel(self): + return math.hypot(self.vel_x, self.vel_y) + + @vel.setter + def vel(self, val): + self.vel_x = val * math.cos(math.radians(self.vel_direction)) + self.vel_y = -val * math.sin(math.radians(self.vel_direction)) + + @property + def vel_direction(self): + return atan2_inv(self.vel_y, self.vel_x) + + @vel_direction.setter + def vel_direction(self, val): + self.vel_x = self.vel * math.cos(math.radians(val)) + self.vel_y = -self.vel * math.sin(math.radians(val)) + + @property + def accln(self): + return math.sqrt(self.accln_x ** 2 + self.accln_y ** 2) + + @accln.setter + def accln(self, val): + self.accln_x = val * math.cos(math.radians(self.accln_direction)) + self.accln_y = -val * math.sin(math.radians(self.accln_direction)) + + @property + def accln_direction(self): + return atan2_inv(self.accln_y, self.accln_x) + + @accln_direction.setter + def accln_direction(self, val): + self.accln_x = self.accln * math.cos(math.radians(val)) + self.accln_y = -self.accln * math.sin(math.radians(val)) + + def distance(self, x, y): + point_ = (x, y) + return distance(self.center, point_) + + def vel_add(self, vel_mag, vel_direction): + ''' vel_mag -> Magnitude of vel. + vel_direction -> direction in degrees. + Adds to current velocity and also returns new (vel_mag,vel_dir).''' + + new_vel_mag, new_vel_dir = vel_add( + (self.vel, self.vel_direction), (vel_mag, vel_direction)) + self.vel_set(new_vel_mag, new_vel_dir) + return self + + def vel_set(self, vel_mag, vel_dir): + self.vel_x = vel_mag * math.cos(math.radians(vel_dir)) + self.vel_y = -vel_mag * math.sin(math.radians(vel_dir)) + return self + + def direction(self, x, y): + point_ = (x, y) + return direction(self.center, point_) + + @property + def list_event_local(self): + if not hasattr(self, "_list_event_local"): + self._list_event_local = [] + return self._list_event_local + + @list_event_local.setter + def list_event_local(self, value): + self._list_event_local = value + + dispatch_events_instance = classmethod(event.dispatch_events_instance) + dispatch_events_self = event.dispatch_events_self + push_event_local = event.push_event_local + _push_event_local_single = event._push_event_local_single + dispatch_events_local = event.dispatch_events_local + clear_event_local = event.clear_event_local + + @classmethod + def register_action(cls, event_name=None, func_to_register=None): + global register_action + register_action(event_name, func_to_register, cls) + + @classmethod + def for_all_instance(cls, func_to_run, flatten): + return_list = [] + for tmp_instance in cls.list_instance: + return_item = func_to_run(tmp_instance) + if flatten == "level-instance": + return_list.extend(return_item) + else: + return_list.append(return_item) + return return_list + + def collide(self, *args, **kwargs): + return self.sprite.collide(*args, **kwargs) + +# always use selfie,not selfie_depreceated most compatible +# make this classmethod,selfie-ridden register_actions as test +# @register_action() + def action_end_step_default(self, ev): + if not(self.x is None) and not(self.y is None): + # Set x_prev, y_prev + self.x_prev = self.x + self.y_prev = self.y + # Accelerate velocity + self.vel_x += self.accln_x + self.vel_y += self.accln_y + # Move x and y + self.x += self.vel_x + self.y += self.vel_y + + def action_begin_step(self, ev): pass - def check_intersect_room_boundary(self): - if event_intersect_room_boundary(self): - action_intersect_room_boundary(self) - def event_outside_room_boundary(self): - ''' Checks if the object (its image bounding box) is totally outside room. - Returns direction constants like TOP, LEFT, BOTTOM, RIGHT. ''' - if (self.x-self.image.bbox.width/2)>fun_Game.room_width: - return RIGHT # all constants defined at global level - elif (self.y-self.image.bbox.height/2)>fun_Game.room_height: - return BOTTOM - elif (self.x+self.image.bbox.width/2)<0: - return LEFT - elif (self.y+self.image.bbox.height/2)<0: - return TOP + def action_draw_default(self, ev): + # print "Drawing" + self.sprite.draw() + def action_quit(self, ev): + # should be made better / overloaded + # IMP: will crash unless weakrefs are used + pygame.quit() # end of fun_Class # Other classes -class Circle(object): - def __init__(self,(x,y),radius,color=None): - self.center=[x,y] - self.radius=radius - self.color=color - @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 bbox(self): - left=self.center_x-self.radius - top=self.center_y-self.radius - width=self.radius*2 - height=self.radius*2 - return pygame.Rect(left, top, width, height) - # todo: remember about the colour. document - center is list unlike tuple in case of rect. - def __repr__(self): - return "{0.__class__}( center={0.center}, radius={0.radius}, color={0.color})".format(self) - def copy(self): - return Circle((self.center_x,self.center_y),self.radius,self.color) - def move(self,move_x,move_y): - return Circle((self.center_x+move_x,self.center_y+move_y),self.radius,self.color) - - def move_ip(self,move_x,move_y): - self.center_x+=move_x - self.center_y+=move_y - - def inflate(self,inflate_radius): - return Circle((self.center_x,self.center_y),self.radius+inflate_radius,self.color) - - def inflate_ip(self,inflate_radius): - self.radius+=inflate_radius - - def is_inside(self,another_circle): - ''' Checks if this circle is fully inside another_circle ''' - return distance( self.center, another_circle.center )another_rect.right or (self.center_y+self.radius)another_rect.bottom ) - - def collide_list_rect(self,list_rect): - ''' Returns True if collides with any of the rect in the list. ''' - for temp_rect in list_rect: - if collide_rect(self,temp_rect): - return True - else: - return False - - def collide_list_all_rect(self,list_rect): - ''' Returns True if collides with all the rect in the list. ''' - for temp_rect in list_rect: - if not collide_rect(self,temp_rect): - return False - else: - return True - - @classmethod - def get_circumcircle_from_rect(cls,another_rect):# create a circle with same center and radius=sqrt(w^2+h^2) - import math - return cls(another_rect.center, math.sqrt(another_rect.width**2+another_rect.height**2)/2) - - @classmethod - def get_incircle_from_rect(cls,another_rect): # radius of incircle is same as the min of width and height (as it's not a square) - import math - return cls(another_rect.center,min(another_rect.width,another_rect.height)) - - # union -needs to be a circle *within* which the union of the bounding box of all the circles can be fit. - # warning about all the union functions: The circles returned are not necessarily the most optimum union circle, but it will surely contain the others. - def _union(self,circle_1,circle_2): - ''' self.__union((new_center_x,new_center_y),new_radius) -> ([x,y],radius) which are the resultant values. ''' - return self.circumcircle_from_rect(circle_1.bbox.union(circle_2.bbox)) - - def union(self,new_circle): - ''' Returns a new circle which is the union of these two circles ''' - return self._union(self,new_circle) - - def union_ip(self,new_circle): - ''' Turns this circle into a circle which is a union of these two circles. ''' - temp_circle=self._union(self,new_circle) - self.center, self.radius = temp_circle.center, temp_circle.radius - - def _unionall(self,circle_1,seq_circle): # list_circle_params : [((x1,y1),radius_1), ((x2,y2),radius_2), ...] - ''' returns ([x,y],radius) for the resultant union of all circles. - sequence_circle must be a sequence of circles. ''' - return self.circumcircle_from_rect(circle_1.bbox.unionall([temp_circle.bbox for temp_circle in seq_circle])) - - def unionall(self,seq_circle): #seq_circle: sequence of circles - return self._unionall(self,seq_circle) - - def unionall_ip(self,seq_circle): - ''' Turns this circle into a circle which is a union of this circles with all circles in seq_circle''' - temp_circle=self._unionall(self,seq_circle) - self.center, self.radius = temp_circle.center, temp_circle.radius - - # draw functions - def draw(self,surface,(x,y)=(None,None),color=None,width=1,use_antialiasing=False): - ''' Draws this Circle on surface. It must be of width and height greater than or equal to its bbox. - (x,y)->co-ordinates whether the center of the Circle should be drawn on the surface. - width argument doesnt work with antialiasing. width=0 makes it filled. - antialiasing may break in later versions as the backend pygame.gfxdraw is experimental. - color falls back first to Circle.color (if present) and then to blue color.''' - if (x,y)==(None,None): - (x,y)=self.center - if not color: - if self.color: - color=self.color - else: - color=pygame.Color("blue") - - if not use_antialiasing: - pygame.draw.circle(surface, color,(x,y), self.radius, width) - else: - pygame.gfxdraw.aacircle(surface, x, y, self.radius, color) - - def get_surface(self,surface=None,flags=0): - ''' Returns a new surface with dimensions of (Circle.bbox.width,Circle.bbox.width) and other properties as surface. - This Circle can be safely drawn on it.''' - if surface: - return pygame.Surface((self.bbox.width,self.bbox.height),flags,surface) - else: - return pygame.Surface((self.bbox.width,self.bbox.height),flags) - - def get_surface_drawn(self,(x,y)=(None,None),color=None,width=1,use_antialiasing=False,surface=None,flags=0): - ''' Returns a surface which has the tiltedRect drwan on it. ''' - temp_surf=self.get_surface(surface,flags) - self.draw(temp_surf,(x,y),color,width,use_antialiasing) - return temp_surf - - # end of Circle class - #start of tiltedRect class -class tiltedRect(pygame.Rect): - # make a metaclass which wraps a lot of functions just like move is defined below. - # functions to be wrapped - move,inflate - # Think about them - clamp,clip,union,fit,contains,collide - # the metaclass can probably also change the __str__ of the class. - # About drawing - use pygame.transform.rotate to get a rotated surface to be drawed. - def __init__(self,(left,top),(width,height),angle,centered_coords=False,color=None): - ''' Angle in degrees. - centered_coords -> whether the (left,top) is actually intended to be (center_x,center_y) or not. - I.e. if centered_coords==True the (left,top) co-ordinates are used as (center_x,center_y) co-ordinates of the tiltedRect''' - self.angle=angle - self.color=color - if centered_coords==True: - left=left-width/2 - top=top-height/2 - super(tiltedRect,self).__init__((left,top),(width,height)) - def __copy__(self): - return tiltedRect((self.left,self.top),(self.width,self.height),self.angle) - def __repr__(self): - return "{0.__class__.__name__}((top={0.top}, left={0.left}), (width={0.width}, height={0.height}), angle={0.angle}, color={0.color})".format(self) - @classmethod - def create_from_rect(cls,another_rect,angle): - return cls((another_rect.left,another_rect.top),(another_rect.width,another_rect.height),angle) - def get_rect(self): - return pygame.Rect((left,top),(width,height)) - def move(self,x,y): - return self.create_from_rect(super(tiltedRect,self).move(x,y),self.angle) - def inflate(self,x,y): - return self.create_from_rect(super(tiltedRect,self).inflate(x,y),self.angle) - def normalize(self): # not sure what this one does - ask others - return self.create_from_rect(super(tiltedRect,self).normalize(),self.angle) - def rotate(self,some_angle): - temp_return=self.__copy__() - temp_return.angle+=some_angle - return temp_return - def rotate_ip(self,some_angle): - self.angle+=some_angle - - def rotate_point_relative(self,x,y,theta=None):#generalise the parameters to accept x1,y1,x2,y2,rot_angle - ''' x,y -> Absolute co-ords of the point to be rotated wrt to the rectangles center. self.centerx,self.centery are internally substracted from them. - theta -> angle (in degrees) by which the point is to be rotated. Default value is self.angle. - Returns absolute positions of the points after rotation. ''' - if theta==None: - theta=self.angle - return self.rotate_point(x,y,self.centerx,self.centery,theta) - - @classmethod - def rotate_point(cls,x,y,origin_x,origin_y,theta): # to be allowed as rotate_point(class) - ''' x,y -> Absolute co-ords of the point to be rotated. origin_x,origin_y are internally substracted from them. - (origin_x,origin_y) -> Absolute co-ords of the origin wrt to which the point will be rotated - theta -> angle (in degrees) by which the point is to be rotated. - Returns absolute positions of the points after rotation. ''' - import math - x_diff=x-origin_x - y_diff=y-origin_y - y_diff=-y_diff # -y_diff because of the inverted nature of y co-ordinate system in pygame - r=math.sqrt(x_diff**2+y_diff**2) - initial_angle=math.atan2(y_diff,x_diff) - final_angle=initial_angle+math.radians(theta) - rotated_x_diff=r*math.cos(final_angle) - rotated_y_diff=r*math.sin(final_angle) - rotated_y_diff = -rotated_y_diff # - used for same reason as above - rotated_x = rotated_x_diff + origin_x - rotated_y = rotated_y_diff + origin_y - return (rotated_x,rotated_y) - - def rotate_point_polar(self,r,initial_angle,theta=None): - ''' theta and initial_angle in radians. - initial_angle ->angle r makes with horizontal. - theta -> angle to be rotated by. ''' - if theta==None: - theta=self.angle - final_angle=initial_angle+theta - rotated_x=r*math.cos(final_angle) - rotated_y=r*math.sin(final_angle) - return (rotated_x,rotated_y) - - @property - def bottomleft_rotated(self): - temp=self.bottomleft - return self.rotate_point_relative(temp[0],temp[1]) - @property - def topleft_rotated(self): - temp=self.topleft - return self.rotate_point_relative(temp[0],temp[1]) - @property - def bottomright_rotated(self): - temp=self.bottomright - return self.rotate_point_relative(temp[0],temp[1]) - @property - def topright_rotated(self): - temp=self.topright - return self.rotate_point_relative(temp[0],temp[1]) - @property - def midleft_rotated(self): - temp=self.midleft - return self.rotate_point_relative(temp[0],temp[1]) - @property - def midright_rotated(self): - temp=self.midright - return self.rotate_point_relative(temp[0],temp[1]) - @property - def midtop_rotated(self): - temp=self.midtop - return self.rotate_point_relative(temp[0],temp[1]) - @property - def midbottom_rotated(self): - temp=self.midbottom - return self.rotate_point_relative(temp[0],temp[1]) - - # related to bounding box - @property - def bbox(self): - list_x=[self.topleft_rotated[0],self.topright_rotated[0],self.bottomleft_rotated[0],self.bottomright_rotated[0]] - list_y=[self.topleft_rotated[1],self.topright_rotated[1],self.bottomleft_rotated[1],self.bottomright_rotated[1]] - min_x=floor(min(list_x)) - max_x=ceil(max(list_x)) - min_y=floor(min(list_y)) - max_y=floor(max(list_y)) - return pygame.Rect((min_x,min_y),(max_x-min_x,max_y-min_y)) - # corners - @property - def corners(self): - ''' List of actual (rotated) corners. ''' - return [self.topleft_rotated,self.topright_rotated,self.bottomright_rotated,self.bottomleft_rotated] - @property - def corners_relative(self): - return [(temp[0]-self.centerx,temp[1]-self.centery) for temp in self.corners] - - # circles related to the rect - def get_circumcircle(self): - Circle.get_circumcircle_from_rect(self.bbox) - def get_incircle(self): - Circle.get_incircle_from_rect(self.bbox) - - # collision events - def collide_point(self,*args): - ''' collidepoint(x,y) -> - collidepoint((x,y)) -> ''' - temp_len=len(args) - temp_x=None - temp_y=None - if temp_len==2: - temp_x=args[0] - temp_y=args[1] - elif temp_len==1: - if len(args[0])==2: - temp_x,temp_y=args[0] - else: - raise TypeError("Arguments are of incorrect type") - else: - raise TypeError("Arguments are of incorrect type") - if temp_x==None or temp_y==None: - raise TypeError("argument must contain two numbers") - return pygame.Rect((self.left,self.top),(self.width,self.height)).collidepoint(self.rotate_point_relative(temp_x,temp_y)) - - def collide_rect(self,rect): - ''' Tests if it collides with rect, which should be instance of rect class. ''' - # First test:whether bbox collides with rect. If not, no collision. - if not self.bbox.colliderect(rect): - return False - else: - # Todo: Else sure test: Need to do line class first. Test if any line of titedRect collides with any line of (instead, the whole) rect. - pass - - # draw functions - def draw(self,surface,(x,y)=(None,None),color=None,width=1,use_antialiasing=False,blend=True): - ''' Draws this tiltedRect on surf_to_draw_on. It must be of width and height greater or equal to its bbox. - (x,y)->co-ordinates whether the lefttop point of its bbox should be drawn on the surface. - width argument doesnt work with antialiasing. width=0 makes it filled. - blend argument works only with antialiasing. The boolean blend argument set to true will blend the shades with existing shades instead of overwriting them. - color falls back first to tiltedRect.color (if present) and then to blue color.''' - if (x,y)==(None,None): - (x,y)=(0,0) - if not color: - if self.color: - color=self.color - else: - color=pygame.Color("blue") - iter_corners=[(temp[0]+x,temp[1]+y) for temp in self.corners_relative] - if not use_antialiasing: - pygame.draw.polygon(surface, color, iter_corners, width) - else: - pygame.draw.aalines(surface, color, True, iter_corners, blend) - def get_surface(self,surface=None,flags=0): - ''' Returns a new surface with dimensions of (tiltedRect.bbox.width,tiltedRect.bbox.width) and other properties as surface. - This tiltedRect can be safely drawn on it.''' - if surface: - return pygame.Surface((self.bbox.width,self.bbox.height),flags,surface) - else: - return pygame.Surface((self.bbox.width,self.bbox.height),flags) - def get_surface_drawn(self,(x,y)=(None,None),color=None,width=1,use_antialiasing=False,blend=True,surface=None,flags=0): - ''' Returns a surface which has the tiltedRect drwan on it. ''' - temp_surf=self.get_surface(surface,flags) - self.draw(temp_surf,(x,y),color,width,use_antialiasing,blend) - return temp_surf - - def collide_tiltedRect(self,another_tiltedRect): - '''Checks whether this tiltedRect collides with another_titltedrect, which must be a instance of titledRect. ''' - # Rotates both tiltedRect by -another_tiltedRect.angle. So, another_tiltedRect becomes a Rect called temp_Rect. - # Then check if the rotated version of this tiltedRect (temp_tiltedRect) collides with the obtained Rect. - temp_tiltedRect=self.rotate(-another_tiltedRect.angle) - temp_Rect=another_tiltedRect.get_rect() - return temp_tiltedRect.colliderect(temp_Rect) - -# functions outside all classes -def get_event_list(event_types=None,further_check_variable_name=None,further_check_value=None,list_to_get=None):#list_to_get is attached to a constant list here - ''' Get the required events from the event_list. - get_event_list([event_types],[further_check_variable_name],further_check_value=None,list_to_get=fun_Game.list_event): - event_types (optional): event_name|list[event_name] - get_event_list() -> returns whole event_list(to be precise, list_to_get) - get_event_list(events) -> returns event_list(to be precise, list_to_get) filtered to contain only "events" - get_event_list([events],further_check_variable_name="var_name",further_check_value=value) -> - returns sub-list containing those elements of get_event_list([events]) for which var_name=value - Potential candidates for list_to_get -> fun_Game.list_event, fun_Game.list_event_old, fun_Game.list_state_all_buttons, fun_Game.list_state_all_buttons_old''' - if list_to_get==None: - list_to_get=fun_Game.list_event_old - if further_check_variable_name: - further_check_enabled=True - else: - further_check_enabled=False - if event_types: - if not hasattr(event_types,"__iter__"): - event_types=[event_types] # if not iterable, make a list out of it - def func_filter(): - list_return=[] - if not event_types: - return list_to_get - for temp_1 in list_to_get: # all events in fun_Game.list_event have a "type" property - for temp_2 in event_types: - if temp_1.type==temp_2: - list_return.append(temp_1) - return list_return - def further_check_filter(temp_list): # returns part of temp_list which passes further_check - list_return=[] - if further_check_enabled: - for temp in temp_list: - if getattr(temp,further_check_variable_name)==further_check_value:# may raise error if further_check_variable_name is not present for all members of the list - list_return.append(temp) - else: - list_return=temp_list - return list_return - return further_check_filter(func_filter()) # returns elements common in fun_Game.list_events and events which are accepted after further_check -# end of get_event_list() - -def distance((x1,y1),(x2,y2)): # Maybe make a non-square rooted function for comparison (faster) - import math - return(math.sqrt((x2-x1)**2+(y2-y1)**2)) - -#following constants are required for the next function -UNION=1 -INTERSECTION=2 -DIFFERENCE=3 -ADD=4 -MAKE_IT_VARIABLE_LIST=5 # for each member (usually objects) in the list, it is replaced by one of its variables -def operation_on_lists(operation,list1,list2=None,variable_name=None): - ''' operation_on_lists(operation, list1, list2) -> list - Does operation involving list1 and list2. - variable_name is required only for MAKE_IT_VARIABLE_LIST operation. It is a string representing the variable name - Valid operations are UNION, INTERSECTION, DIFFERENCE, ADD, MAKE_IT_VARIABLE_list ''' - def intersection(): - if list2==None: - raise TypeError("second argument i.e. list2 should be a list") - list_return=[] - for temp1 in list1: - for temp2 in list2: - if temp1==temp2: - list_return.append(temp1) - return list_return - def union(): - if list2==None: - raise TypeError("second argument i.e. list2 should be a list") - list_return = uniquelist() - list_return.extend(list1) - list_return.extend(list2) - return list_return - def add(): - if list2==None: - raise TypeError("second argument i.e. list2 should be a list") - list_return=[] - list_return.append(list1) - list_return.append(list2) - return list_return - def difference(): - if list2==None: - raise TypeError("second argument i.e. list2 should be a list") - list_return=[] - list_intersection=intersection() - for temp1 in list1: - if temp1 not in list_intersection: - list_return.append(temp1) - return list_return - def make_it_variable_list(): # for each member (usually objects) in the list, it is replaced by one of its variables - if variable_name==None: - raise TypeError("variable_name should be a string") - if not list2==None: - raise TypeError("list2 should be None") - return_list=[getattr(temp,variable_name) for temp in list1] - - if operation==UNION: - return union() - elif operation==INTERSECTION: - return intersection() - elif operation==DIFFERENCE: - return difference() - elif operation==ADD: - return add() - elif operation==MAKE_IT_VARIABLE_LIST: - return make_it_variable_list() - else: - raise ValueError("Invalid operation name. Valid operations are UNION, INTERSECTION, DIFFERENCE.") -# end of operation_on_lists() -#Todo: see below -# 1. Allow rect-like and circle-like constructs wherever rect and circle are expected. +# Todo: see below +# Collision helper event_collision funcs (Done, for circle) +# Allow collision with "all" +# More collision func like GM +# More extended draw funcs +# Make img sprite +# Use rects retuned from blit to display.update +# +# 1. (Think) Allow rect-like and circle-like constructs wherever rect and circle are expected. # 2. Do all sort of type-checking. +# 3. Use sprites and groups in pygame as containers +# Imp: Allow debugging with event handlers and others with @debug + # Design tips I will use later +# use weakrefs as much as possible # IMP: always derive classes from object. Else, in python 2.x, they become old-style class which sucks. # use tuples instead of lists wherver possible because they are usually faster to create # learn about generators @@ -794,4 +459,4 @@ def operation_on_lists(operation,list1,list2=None,variable_name=None): # partial() makes a function with more number of argument to less number of argument # use for/else instead of exit based on flags # look at dict.iteritems() and dict.setdefaults -# learn defaultdicts and collections module \ No newline at end of file +# learn defaultdicts and collections module diff --git a/base.pyc b/base.pyc new file mode 100644 index 0000000..f666824 Binary files /dev/null and b/base.pyc differ diff --git a/base_old.py b/base_old.py new file mode 100644 index 0000000..d40d038 --- /dev/null +++ b/base_old.py @@ -0,0 +1,539 @@ +# only for python 2.x +# VERY IMP: Use weakrefs instead of list for fun_Game.list_class,\ +# fun_Class.list_instance and fun_Class.list_update_func + +from uniquelist import uniquelist # imports uniquelist +import pygame +from pygame.locals import * # imports the constants +import math +from utils import register_class, meta_accessor_class, register_event, selfie, KW_EVENTS_IB, sort_events, append_user_events, fuzzy_match_event_name, KW_EVENTS_ALL, rect_to_pygame_rect +# import utils +from functools import partial +from sprite_types import Circle, Rect +import logging +from copy import deepcopy +from math_utils import atan2_inv + +logging.basicConfig(level=logging.DEBUG) + +IS_TEST_MODE = True + +# Mouse constants +B_LEFT_CLICK = 1 +B_RIGHT_CLICK = 3 +B_MIDDLE_CLICK = 2 +B_SCROLLUP = 4 +B_SCROLLDOWN = 5 + +# Direction constants +TOP = 1 +LEFT = 2 +BOTTOM = 3 +RIGHT = 4 + + +class fun_Game(object): + list_class = uniquelist() + list_event = [] # event_list of this step + list_event_old = [] # event_list of the previous step + # A sequence of boolean representing the state of every key + list_state_all_buttons = [] + # A sequence of boolean representing the state of every key of previous + # step + list_state_all_buttons_old = [] + + def __init__(self, width, height, fps=30): + pygame.init() + self.fps = fps + self.clock = pygame.time.Clock() + self.width = width + self.height = height + self.test_init() + + @property + def w(self): + return self.width + + @w.setter + def w(self, val): + self.width = val + + @property + def h(self): + return self.height + + @h.setter + def h(self, val): + self.height = val + +# @classmethod +# def _register_class_(cls, class_to_register): +# ''' Registers class_to_register to its list_class ''' +# cls.list_class.append(class_to_register) + @classmethod + def register_class(cls, cls_to_register): + cls.list_class.append(cls_to_register) + + @classmethod + def update_all_class(cls): + for tmp_cls in cls.list_class: + tmp_cls.update_instances(cls.list_event) + + @classmethod + def process_event_list(cls, list_event_): + append_user_events(list_event_) + cls.list_event.sort(sort_events) + + @classmethod + def _cache_event_list(cls): + ''' Cache event_list in every step. + All events in fun_Game.list_event have a "type" property which are the usual constants. ''' + cls.list_event_old = cls.list_event # move current list to old list + cls.list_state_all_buttons_old = cls.list_state_all_buttons + + cls.list_event = pygame.event.get() + cls.process_event_list(cls.list_event) + # Returns a sequence of boolean representing the state of every key. + # Use key constant values for indexing. + cls.list_state_all_buttons = pygame.key.get_pressed() + # print cls.list_event # debug message + # control access to list_event + # with getter, do something crazy for filtering + # or a filtering function + + @classmethod + def for_all_instance(cls, func_to_run, flatten): + ''' Note: flatten can be "level-instance" or "level-class" or False''' + return_list = [] + for tmp_cls in cls.list_class: + return_item = tmp_cls.for_all_instance( + func_to_run, flatten=flatten) + if flatten == "level-instance" or flatten == "level-class": + return_list.extend(return_item) + else: + return_list.append(return_item) + return return_list + + @classmethod + def for_all_class(cls, func_to_run, flatten): + ''' Note: flatten can be True or False''' + return_list = [] + for tmp_class in cls.list_class: + return_item = func_to_run(tmp_class) + if flatten: + return_list.extend(return_item) + else: + return_list.append(return_item) + return return_list + + @staticmethod + def collect_self_clear_rect(self): + _list_clear_rect = [] + _list_rendered_sprite = [] + # perf hog: search for better way to remove list items iteratively + for tmp_old_sprite in self.list_rendered_sprite: + if (not tmp_old_sprite.get("still_valid")): + # returns bbox of all list_rendered_sprite that are not still_valid, after removing them from that list + # so, list now contains only of the older sprites still_valid + tmp_clear_rect_bbox = tmp_old_sprite["bbox"] + _list_clear_rect.append(tmp_clear_rect_bbox) + else: + _list_rendered_sprite.append(tmp_old_sprite) + if ("still_valid" in tmp_old_sprite): + tmp_old_sprite.pop("still_valid") + + # in next step : + # _list_rendered_sprite.append(self.list_dirty_sprite) + # self.list_dirty_sprite=[] + self.list_rendered_sprite = _list_rendered_sprite + return _list_clear_rect + + @staticmethod + def collect_self_rendered_sprite(self): + return self.list_rendered_sprite + + @staticmethod + def collect_self_dirty_sprite(self): + return self.list_dirty_sprite + + @staticmethod + def move_dirty_to_rendered(self): + self.list_rendered_sprite.extend(self.list_dirty_sprite) + self.list_dirty_sprite = [] + return None + + def blit_all_class(self): + # step 1: collect all clear rects + # i.e. remove last_rendered_rects which are not still_valid and return them + # also move to_be_rendered_sprites into that list and clear dirty list + list_clear_rect = self.for_all_instance( + self.collect_self_clear_rect, flatten="level-instance") + list_rendered_sprite = self.for_all_instance( + self.collect_self_rendered_sprite, flatten="level-instance") + list_dirty_sprite = self.for_all_instance( + self.collect_self_dirty_sprite, flatten="level-instance") + + # step 2: check if any clear_rect intersects with last_rendered_rects, + # return a list of dirty overlay for those last_rendered_rects. + # also if it is contained (totally within), mark clear_rect for removal + list_dirty_overlay_sprite = [] + list_cancelled_clear_rect = [] + for tmp_old_sprite in list_rendered_sprite: + for tmp_clear_rect in list_clear_rect: + tmp_intersect_rect = tmp_clear_rect.clip( + tmp_old_sprite["bbox"]) + if tmp_intersect_rect.w > 0 and tmp_intersect_rect.h > 0: + # make blit funcs capable of taking a clip rect, will be appplied before blitting +## print("explicit blit") + copy_old_sprite = deepcopy(tmp_old_sprite) + copy_old_sprite["clip_rect"] = tmp_intersect_rect + list_dirty_overlay_sprite.append(copy_old_sprite) + + if tmp_old_sprite["bbox"].contains(tmp_clear_rect): + # Cancel clear rect + list_cancelled_clear_rect.append(tmp_clear_rect) + + else: + # No explicit blit + pass + # step 3: for every clear_rect not marked for removal, do the + # clear_rects. +# print "Clear_rects= ",len(list_clear_rect)-len(list_cancelled_clear_rect) + i = 0 + for tmp_clear_rect in list_clear_rect: + # convert to pygame rect + if not (tmp_clear_rect in list_cancelled_clear_rect): + self.screen.fill(pygame.Color(0, 0, 0, 1), tmp_clear_rect) + i += 1 + print "i=", i + + # step 4: do blit for all dirty_sprite and dirty_overlay + for tmp_dirty_sprite in list_dirty_sprite: + tmp_dirty_sprite["sprite"].blit(self.screen) + +# print "Dirty_overlays= ",len(list_dirty_overlay_sprite) +# print list_dirty_overlay_sprite + for tmp_overlay_sprite in list_dirty_overlay_sprite: + new_overlay_sprite = tmp_overlay_sprite[ + "sprite"].__class__(**tmp_overlay_sprite["config"]) + new_overlay_sprite.blit( + self.screen, clip_rect=tmp_overlay_sprite["clip_rect"]) + + # step 5: update display + list_display_update = [tmp_dirty_sprite["bbox"] + for tmp_dirty_sprite in list_dirty_sprite] + list_clear_rect + pygame.display.update(list_display_update) +# if not (list_dirty_sprite[0]["sprite"].bbox in list_display_update): +# print("problem") + + # step 6: cleaning jobs + self.for_all_instance(self.move_dirty_to_rendered, flatten=False) +# pygame.display.flip() + + def test_init(self): + ''' Empty function to be overloaded for extra init on test mode. ''' + pass + + def test_update(self): + ''' Empty function to be overloaded for extra draw on test mode. ''' + pass + + def run(self): + self.screen = pygame.display.set_mode([self.width, self.height]) + frame_no = 0 + while True: + # Todo: Always exit on event_close : make it default behavior, + # although override-able + frame_no += 1 + # print frame_no + self._cache_event_list() + self.update_all_class() + self.blit_all_class() + self.test_update() + # VERY IMPORTANT: Do a spare first step so that every variable / list gets initialized well. + # Start instant creation and execution from second step. + # IMPORTANT: First do all fun_Game related stuff before dealing + # with game objects. + self.clock.tick(self.fps) + + # Todo - Add crazy functions to allow other objects and instances finding + # like GM + # Todo - Easy way to take screenshot and save it as pic + # Todo: Game testing module. Lets you compare screenshots and compare values with pixelarray or surface.get_at + # Also allows accessing different config values. + + + # class fun_Game ends here +# print(fun_Game) + +# global register_event cached,as it will also be used as classmethod +# should use register_event=_register_event at end of all class defn + +meta_fun_Class = meta_accessor_class + + +@register_class(fun_Game) +class fun_Class(object): + # think about this: priority_order and depth + # by default, depends on "when class was defined" + # cls.priority_order = cls.depth = len(fun_Game.list_class) + __metaclass__ = meta_fun_Class + + # Below lines moved to meta_fun_Class + # list_instance = uniquelist() + # list_check_func = uniquelist() + # dict_action_func -> dict of action funcs, key=event_numb like KEYDOWN + # dict_action_func = {} + + sprite_defaults = { + "type": Rect, + "params": { + "size": (40, 40), + "color": None + } + } + + def __init__(self, x=None, y=None): + # use sprite as a general term, img->one kind of sprite, rect, circle + self.x_start = x + self.y_start = y + self.x = x + self.y = y + self.x_prev = x + self.y_prev = y + self.vel_x, self.vel_y, self.accln_x, self.accln_y = 0, 0, 0, 0 + + if (self.sprite_defaults and self.sprite_defaults.get("type")): + if (x is None) or (y is None): + raise AttributeError( + "fun_Class with sprite_defaults must have x,y") + + self.sprite = self.sprite_defaults.get("type").from_container_obj( + container_obj=self, **self.sprite_defaults["params"]) + self.list_dirty_sprite = [] + # list_rendered_sprite => sprites that are effectively on the + # screen now + self.list_rendered_sprite = [] + else: + self.sprite = None + self.list_instance.append(self) + # think about self.action_create() + + @property + def vel(self): + return math.sqrt(self.vel_x ** 2 + self.vel_y ** 2) + + @vel.setter + def vel(self, val): + self.vel_x = val * math.cos(math.radians(self.vel_direction)) + self.vel_y = val * math.sin(math.radians(self.vel_direction)) + + @property + def vel_direction(self): + return atan2_inv(self.vel_y, self.vel_x) + + @vel_direction.setter + def vel_direction(self, val): + self.vel_x = self.vel * math.cos(math.radians(val)) + self.vel_y = self.vel * math.sin(math.radians(val)) + + @property + def accln(self): + return math.sqrt(self.accln_x ** 2 + self.accln_y ** 2) + + @accln.setter + def accln(self, val): + self.accln_x = val * math.cos(math.radians(self.accln_direction)) + self.accln_y = val * math.sin(math.radians(self.accln_direction)) + + @property + def accln_direction(self): + return atan2_inv(self.accln_y, self.accln_x) + + @accln_direction.setter + def accln_direction(self, val): + self.accln_x = self.accln * math.cos(math.radians(val)) + self.accln_y = self.accln * math.sin(math.radians(val)) + + @classmethod + def update_instances(cls, list_event): + '''calls update_self for all instances.''' + for tmp_instance in cls.list_instance: + tmp_instance.update_self(list_event) + + def update_self(self, list_event): + # print list_event + for each_ev in list_event: +# print self.dict_action_func + if each_ev.type in self.dict_action_func: + for each_action in self.dict_action_func[each_ev.type]: + # similar as self.action_step() + each_action(self, each_ev) + + @classmethod + def _check_event(cls, ev_name_or_type, new_or_old): + ''' ev_name_or_type => name_or_type(numb)_of_event + new_or_old => current_event_list or old_event_list ''' + matched_events = [] + if new_or_old == "new": + list_to_check = cls.game_class.list_event + elif new_or_old == "old": + list_to_check = cls.game_class.list_event_old + else: + raise AttributeError("new_or_old must be 'new' or 'old'.") + if type(ev_name_or_type) is int: + ev_type = ev_name_or_type + elif type(ev_name_or_type) is str: + ev_name = fuzzy_match_event_name(ev_name_or_type, "event") + ev_type = KW_EVENTS_ALL[ev_name] + for ev in list_to_check: + if ev.type == ev_type: + matched_events.append(ev) + return matched_events + + @classmethod + def check_event(cls, ev_name_or_type): + return cls._check_event(ev_name_or_type, "new") + + @classmethod + def check_event_old(cls, ev_name_or_type): + return cls._check_event(ev_name_or_type, "old") + + @classmethod + def register_event(cls, event_name=None, func_to_register=None): + global register_event + register_event(event_name, func_to_register, cls) + + @classmethod + def for_all_instance(cls, func_to_run, flatten): + return_list = [] + for tmp_instance in cls.list_instance: + return_item = func_to_run(tmp_instance) + if flatten == "level-instance": + return_list.extend(return_item) + else: + return_list.append(return_item) + return return_list + + def collide(self, *args, **kwargs): + return self.sprite.collide(*args, **kwargs) + +# always use selfie,not selfie_depreceated most compatible +# make this classmethod,selfie-ridden register_events as test +# @register_event() + def action_end_step_default(self, ev): + if not(self.x is None) and not(self.y is None): + # Set x_prev, y_prev + self.x_prev = self.x + self.y_prev = self.y + # Accelerate velocity + self.vel_x += self.accln_x + self.vel_y += self.accln_y + # Move x and y + self.x += self.vel_x + self.y += self.vel_y + + def action_begin_step(self, ev): +# print "Calling begin_step" + pass + + def action_JOY_BUTTON_UP(self, ev): + print "calling JOY_BUTTON_UP" + + def action_key_down(self, ev): + print "Calling key_down" + + def action_end_step(self, ev): +# print "Calling end step" + pass + + def action_draw_default(self, ev): + # print "Drawing" + self.sprite.draw() + # end of fun_Class +# Other classes + + +# functions outside all classes + + +# list_to_get is attached to a constant list here +def get_event_list(event_types=None, further_check_variable_name=None, further_check_value=None, list_to_get=None): + ''' Get the required events from the event_list. + get_event_list([event_types],[further_check_variable_name],further_check_value=None,list_to_get=fun_Game.list_event): + event_types (optional): event_name|list[event_name] + get_event_list() -> returns whole event_list(to be precise, list_to_get) + get_event_list(events) -> returns event_list(to be precise, list_to_get) filtered to contain only "events" + get_event_list([events],further_check_variable_name="var_name",further_check_value=value) -> + returns sub-list containing those elements of get_event_list([events]) for which var_name=value + Potential candidates for list_to_get -> + fun_Game.list_event, fun_Game.list_event_old, fun_Game.list_state_all_buttons, fun_Game.list_state_all_buttons_old''' + if list_to_get is None: + list_to_get = fun_Game.list_event_old + if further_check_variable_name: + further_check_enabled = True + else: + further_check_enabled = False + if event_types: + if not hasattr(event_types, "__iter__"): + # if not iterable, make a list out of it + event_types = [event_types] + + def func_filter(): + list_return = [] + if not event_types: + return list_to_get + # all events in fun_Game.list_event have a "type" property + for temp_1 in list_to_get: + for temp_2 in event_types: + if temp_1.type == temp_2: + list_return.append(temp_1) + return list_return + + # returns part of temp_list which passes further_check + def further_check_filter(temp_list): + list_return = [] + if further_check_enabled: + for temp in temp_list: + # may raise error if further_check_variable_name is not present + # for all members of the list + if getattr(temp, further_check_variable_name) == further_check_value: + list_return.append(temp) + else: + list_return = temp_list + return list_return + # returns elements common in fun_Game.list_events and events which are + # accepted after further_check + return further_check_filter(func_filter()) +# end of get_event_list() + +# Todo: see below +# Collision helper event_collision funcs (Done, for circle) +# Allow collision with "all" +# More collision func like GM +# More extended draw funcs +# Make img sprite +# Use rects retuned from blit to display.update +# +# 1. (Think) Allow rect-like and circle-like constructs wherever rect and circle are expected. +# 2. Do all sort of type-checking. +# 3. Use sprites and groups in pygame as containers +# Imp: Allow debugging with event handlers and others with @debug + +# Design tips I will use later +# use weakrefs as much as possible +# IMP: always derive classes from object. Else, in python 2.x, they become old-style class which sucks. +# use tuples instead of lists wherver possible because they are usually faster to create +# learn about generators +# named tuples, arrays are better. use more of them. named tuples can be accessed. +# refactor code into proper modules as-well-as use better named functions and variables +# use enumerate() for getting the index and value of elements while looping through lists. This mistake done atleast 1 time. +# use xrange instead of range. It produces one at a time. range is replaced by xrange in python 3.x +# use reversed() for backward loops, sorted() for sorted order. +# instead of zip(), use izip(). +# use key instead of comparator +# use iter(iter_type,sentinel_value) instead of for loops +# partial() makes a function with more number of argument to less number of argument +# use for/else instead of exit based on flags +# look at dict.iteritems() and dict.setdefaults +# learn defaultdicts and collections module diff --git a/base_old.pyc b/base_old.pyc new file mode 100644 index 0000000..9b293c2 Binary files /dev/null and b/base_old.pyc differ diff --git a/base_old_0.py b/base_old_0.py new file mode 100644 index 0000000..6cc0e06 --- /dev/null +++ b/base_old_0.py @@ -0,0 +1,1005 @@ +# only for python 2.x +# VERY IMP: Use weakrefs instead of list for fun_Game.list_class,\ +# fun_Class.list_instance and fun_Class.list_update_func + +from uniquelist import uniquelist # imports uniquelist +import pygame +from pygame.locals import * # imports the constants +import math +from utils import register, KW_EVENTS, fuzzy_match_event_name +from functools import partial +import logging + +logging.basicConfig(level=logging.DEBUG) + +IS_TEST_MODE = True + +# Mouse constants +B_LEFT_CLICK = 1 +B_RIGHT_CLICK = 3 +B_MIDDLE_CLICK = 2 +B_SCROLLUP = 4 +B_SCROLLDOWN = 5 + +# Direction constants +TOP = 1 +LEFT = 2 +BOTTOM = 3 +RIGHT = 4 + + +class fun_Game(object): + list_class = uniquelist() + list_event = [] # event_list of this step + list_event_old = [] # event_list of the previous step + # A sequence of boolean representing the state of every key + list_state_all_buttons = [] + # A sequence of boolean representing the state of every key of previous + # step + list_state_all_buttons_old = [] + + def __init__(self, width, height, fps=30): + pygame.init() + self.fps = fps + self.clock = pygame.time.Clock() + self.width = width + self.height = height + self.test_init() + + def update_all_class(self): + for temp_cls in fun_Game.list_class: + for temp_count in temp_cls.list_instance: + temp_count.update() + + def _cache_event_list(self): + ''' Cache event_list in every step. + All events in fun_Game.list_event have a "type" property which are the usual constants. ''' + fun_Game.list_event_old = fun_Game.list_event # move current list to old list + fun_Game.list_state_all_buttons_old = fun_Game.list_state_all_buttons + + fun_Game.list_event = pygame.event.get() + # Returns a sequence of boolean representing the state of every key. + # Use key constant values for indexing. + fun_Game.list_state_all_buttons = pygame.key.get_pressed() + # print fun_Game.list_event # debug message + # control access to list_event + # with getter, do something crazy for filtering + # or a filtering function + + def test_init(self): + ''' Empty function to be overloaded for extra init on test mode. ''' + pass + + def test_update(self): + ''' Empty function to be overloaded for extra draw on test mode. ''' + pass + + def run(self): + self.screen = pygame.display.set_mode([self.width, self.height]) + while True: + # Todo: Add other update-related functions, there should be separate render_all_class() + # Todo: Always exit on event_close : make it default behavior, + # although override-able + self._cache_event_list() + self.update_all_class() + self.test_update() + # VERY IMPORTANT: Do a spare first step so that every variable / list gets initialized well. Start instant creation and execution from second step. + # IMPORTANT: First do all fun_Game related stuff before dealing + # with game objects. + self.clock.tick(self.fps) + # Should do display.update() + pygame.display.flip() # debug statement, should use display.update + + # Todo - Add crazy functions to allow other objects and instances finding + # like GM + # Todo - Easy way to take screenshot and save it as pic + # Todo: Game testing module. Lets you compare screenshots and compare values with pixelarray or surface.get_at + # Also allows accessing different config values. + + + # class fun_Game ends here + + +class meta_fun_Class(type): + + def __new__(cls, name, base, clsdict): + temp_class = type.__new__(cls, name, base, clsdict) + temp_class.__class__ = type + fun_Game.list_class.append(temp_class) + # by default, priority_order and depth depends on "when class was + # defined" + cls.priority_order = cls.depth = len(fun_Game.list_class) + + return temp_class + + +class fun_Class(object): + __metaclass__ = meta_fun_Class + list_instance = uniquelist() + # list_update_func stores the list of functions to call in the given order. + # It can be used for injecting custom user functions (remember order). + # action_draw not added as it depends on depth + list_update_func = uniquelist() + # all the check functions added here. what they do: if event_: + # action_ + + list_check_func = uniquelist() + # dict_action_func -> dict of action funcs, key=event_numb like KEYDOWN + dict_action_func = {} + + def __init__(self, x, y, img=None): + self.x = x + self.y = y + self.img = img # img should be a fun_Image, but lets do that later + fun_Class.list_instance.append(self) + self.action_create() + + def update(self): + '''calls all functions in list_update_func in the given order''' + tmp_func = None + for tmp_func in fun_Class.list_update_func: + tmp_func(self) + + def draw(self): + # do some blitting depending on self.img + pass + + @classmethod + def register_event(cls, event_name=None, func_to_register=None): + ''' + @fun_Class.register_event() # @fun_Class.register_event === @fun_Class.register_event() + def event_key_down(ev): + pass + OR + @fun_Class.register_event(event_name) # event_name=KEYDOWN (or "key_down") + def whatever(ev): + pass + OR + fun_Class.register_event(event_name,func) + ''' + if (event_name is None) and (func_to_register is None): + # i.e No param passed, used as decorator (1st step of ex. 1) + logging.info("Nothing passed") + return cls.register_event + elif (not(event_name is None)) and (func_to_register is None): + # i.e. one param is passed, maybe func or (str or int) + logging.info("Event Name only passed") + if isinstance(event_name, int) or isinstance(event_name, str): + # i.e. 1st parm-> str or int, is event name, acts as decorator + # (2nd ex.) + # dont hardcode func name- use metaclass + logging.info("Event Name is event_name") + return partial(cls.register_event, event_name) + elif hasattr(event_name, "__call__"): + # first param is func, actually func_to_register. + # So get event_name from func_name (2nd step of 1st ex.) + logging.info("Event Name is actually func") + func_to_register = event_name + event_name = fuzzy_match_event_name(func_to_register.func_name) + return cls.register_event(event_name, func_to_register) + else: + raise TypeError("event_name must be (str or int) or func") + else: + # Both params present + logging.info("Both param passed") + + if isinstance(event_name, str): + event_name = fuzzy_match_event_name(event_name) + event_numb = KW_EVENTS[event_name] + elif isinstance(event_name, int): + event_numb = event_name + else: + raise TypeError("event_name should be str or int") + + logging.info("Registering func") + if not (event_numb in cls.dict_action_func): + cls.dict_action_func[event_numb] = [] + cls.dict_action_func[event_numb].append(func_to_register) + + return func_to_register + + @register(list_update_func) + def action_begin_step(self): + pass + + @register(list_update_func) + def action_step(self): + tmp = self.event_mouse_click() + if tmp: + print tmp + print(self.mouse_get_focused()) + + @register(list_update_func) + def action_end_step(self): + pass + # creation and destroy event + + def action_create(self): + ''' Better to use this function than overloading __init__() ''' + pass + + def action_destroy(self): + ''' Executed when the instance is destroyed ''' + pass + + def destroy(self): + action_destroy(self) + + # checks for all standard events -- functions named like check_ + # what they do: if event_: action_ + # so lets define event_,action_,check_ + # for each + + # keyboard handling (later move to keyboard module) + + # occurs if the key was held down continously since the last step + def event_key_pressed(self, key_value): # check if it works + if get_event_list(list_to_get=fun_Game.list_state_all_buttons)[key_value] \ + and get_event_list(list_to_get=fun_Game.list_state_all_buttons_old)[key_value]\ + and (not get_event_list(event_types=KEYUP, further_check_variable_name="key", further_check_value=key_value))\ + and (not get_event_list(event_types=KEYUP, further_check_variable_name="key", further_check_value=key_value, list_to_get=fun_Game.list_event_old)): + return True + + def action_key_pressed(self, key_value): + pass + + def check_key_pressed(self): + pass # Todo: think about how to do it + + # occurs if the key is held at the moment (live) + def event_key_pressed_live(self, key_value): # check if it works + ''' Returns True if the key (represented by key_code) is currently pressed. + Problem is that this function is live. Returns True if the key is pressed at that moment. + So, for example, two calls in two consecutive steps may return True although the key has been left in the meantime. ''' + temp_all_buttons = get_event_list( + list_to_get=fun_Game.list_state_all_buttons) # Returns a sequence of boolean representing the state of every key. Use key constant values for indexing. + if temp_all_buttons[key_value]: # indexing by key_value + return True + + def action_key_pressed_live(self, key_value): + pass + + def check_key_pressed_live(self): + # Returns a sequence of boolean representing the state of every key. + # Use key constant values for indexing. + temp_all_buttons = get_event_list( + list_to_get=fun_Game.list_state_all_buttons) + # temp represents the key code in numbers as it is the index of + # list_state_all_buttons indirectly + for temp in range(len(temp_all_buttons)): + if temp_all_buttons[temp]: + self.action_key_pressed_live(temp) # temp is the keycode + + # occurs if a key press occurs (i.e. pushed down) + # key repetations may occur as the list is cached per step and there might + # be multiple (same key)press per step. + def event_key_down(self, key=None): + if key is None: + return get_event_list(KEYDOWN) + else: + return get_event_list(KEYDOWN, "key", key) + + def action_key_down(self, key): + pass + + def check_key_down(self): + for temp in get_event_list(KEYDOWN): + action_key_down(self, temp.key) + + # probably wierd thing to do - define all <>_key_down as equivalent to + # <>_key_press + event_key_press = event_key_down + action_key_press = action_key_down + check_key_press = check_key_down + + # occurs if key is released + def event_key_up(self, key=None): + if key is None: + return get_event_list(KEYUP) + else: + return get_event_list(KEYUP, "key", key) + + def action_key_up(self, key): + pass + + def check_key_up(self): + for temp in get_event_list(KEYUP): + action_key_up(self, temp.key) + # Todo: Do other key and keyboard related functions (look at GM for related functions) + # Mouse handling (later move it to mouse module) + # Mouse constants (defined at top - move them to a constants module, which will be imported in the global namespace) + # mouse click + + def event_mouse_click(self, button=None): + if button is None: + return get_event_list(event_types=MOUSEBUTTONDOWN) + else: + return get_event_list(event_types=MOUSEBUTTONDOWN, further_check_variable_name="button", further_check_value=button) + + def action_mouse_click(self, button): + pass + + def check_mouse_click(self): + list_temp = get_event_list(event_types=MOUSEBUTTONDOWN) + for temp in list_temp: + action_mouse_click(temp.button) + # mouse get pressed + + def event_mouse_get_pressed(self, button=None): + ''' event_mouse_get_pressed() -> tuple looking like (1,0,0) i.e. ( left_click_state, middle_click_state, right_click_state ). + Use an index which is (corresponding_constant - 1). For eg event_mouse_get_pressed()[0] returns left click state. + event_mouse_get_pressed(button) -> Returns 0 or 1 depending on pressed or not. ''' + if button is None: + return pygame.mouse.get_pressed() + else: + # because the tuple returned has o-based indexing + return pygame.mouse.get_pressed()[button - 1] + # end of mouse handling + # other mouse-based functions + + def mouse_get_focused(self): + return pygame.mouse.get_focused() + + def mouse_set_visible(self, bool_value): + return pygame.mouse.set_visible(bool_value) + + def mouse_get_rel(self): + ''' Returns relative movement of the mouse since the last call to this function. + mouse_get_rel() -> (x,y) ''' + return pygame.mouse.get_rel() + + def mouse_set_cursor_image(self, image=None): + pass # todo: complete after the image part is done + # implement mouse_pos as variable + + @property + def mouse_pos(self): + ''' mouse_pos -> (x,y) + mouse_pos = [x,y] -> Sets cursor position ''' + return pygame.mouse.get_pos() + + @mouse_pos.setter + def mouse_pos(self, val): + ''' val should be a list [x,y] ''' + if not isinstance(val, list): + raise TypeError("val should be a list [x,y]") + pygame.mouse.set_pos(val) + + # implement mouse_x as variable + @property + def mouse_x(self): + ''' mouse_x -> x + mouse_x = some_x -> Sets cursor x position ''' + return pygame.mouse.get_pos()[0] # get_pos -> (x,y) + + @mouse_x.setter + def mouse_x(self, x): + pygame.mouse.set_pos([x, mouse_y]) + + # implement mouse_y as variable + @property + def mouse_y(self): + ''' mouse_y -> y + mouse_y = some_y -> Sets cursor y position ''' + return pygame.mouse.get_pos()[1] # get_pos -> (x,y) + + @mouse_y.setter + def mouse_y(self, y): + pygame.mouse.set_pos([mouse_x, y]) + # end of mouse-related function + # other various events + + # todo: test after image part is done + def event_intersect_room_boundary(self): + ''' Checks if the object (its image bounding box) is intersecting the room boundary, but not totally outside the room. + Returns direction constants like TOP, LEFT, BOTTOM, RIGHT. ''' + if (self.x + self.image.bbox.width / 2) > fun_Game.room_width and not (self.x - self.image.bbox.width / 2) > fun_Game.room_width: + return RIGHT # all constants defined at global level + elif (self.y + self.image.bbox.height / 2) > fun_Game.room_height and not (self.y - self.image.bbox.height / 2) > fun_Game.room_height: + return BOTTOM + elif (self.x - self.image.bbox.width / 2) < 0 and not (self.x + self.image.bbox.width / 2) < 0: + return LEFT + elif (self.y - self.image.bbox.height / 2) < 0 and not (self.y + self.image.bbox.height / 2) < 0: + return TOP + + def action_intersect_room_boundary(self): + pass + + def check_intersect_room_boundary(self): + if event_intersect_room_boundary(self): + action_intersect_room_boundary(self) + + def event_outside_room_boundary(self): + ''' Checks if the object (its image bounding box) is totally outside room. + Returns direction constants like TOP, LEFT, BOTTOM, RIGHT. ''' + if (self.x - self.image.bbox.width / 2) > fun_Game.room_width: + return RIGHT # all constants defined at global level + elif (self.y - self.image.bbox.height / 2) > fun_Game.room_height: + return BOTTOM + elif (self.x + self.image.bbox.width / 2) < 0: + return LEFT + elif (self.y + self.image.bbox.height / 2) < 0: + return TOP + + # end of fun_Class +# Other classes + + +class Circle(object): + + def __init__(self, (x, y), radius, color=None): + self.center = [x, y] + self.radius = radius + self.color = color + + @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 bbox(self): + left = self.center_x - self.radius + top = self.center_y - self.radius + width = self.radius * 2 + height = self.radius * 2 + return pygame.Rect(left, top, width, height) + # todo: remember about the colour. document - center is list unlike tuple + # in case of rect. + + def __repr__(self): + return "{0.__class__}( center={0.center}, radius={0.radius}, color={0.color})".format(self) + + def copy(self): + return Circle((self.center_x, self.center_y), self.radius, self.color) + + def move(self, move_x, move_y): + return Circle((self.center_x + move_x, self.center_y + move_y), self.radius, self.color) + + def move_ip(self, move_x, move_y): + self.center_x += move_x + self.center_y += move_y + + def inflate(self, inflate_radius): + return Circle((self.center_x, self.center_y), self.radius + inflate_radius, self.color) + + def inflate_ip(self, inflate_radius): + self.radius += inflate_radius + + def is_inside(self, another_circle): + ''' Checks if this circle is fully inside another_circle ''' + return distance(self.center, another_circle.center) < self.radius + + def is_inside_mutual(self, another_circle): + ''' Checks if this circle is fully inside another_circle and also the opposite. + 2x Faster than using is_inside for both of them. ''' + return (distance(self.center, another_circle.center) < max(self.radius, another_circle.radius)) + + def collide_circle(self, another_circle): + ''' Checks if this circle intersects with another_circle. ''' + return (distance(self.center, another_circle.center) <= (self.radius + another_circle.radius)) + + def collide_point(self, (point_x, point_y)): + ''' Checks if (point_x, point_y) is inside this circle. ''' + return (distance(self.center, (point_x, point_y)) <= self.radius) + + def collide_list_circle(self, list_circle): + ''' Checks if this circle intersects with any of the circles in circle_list. ''' + for circle in list_circle: + if collide_circle(self, circle): + return True + else: # else of the for loop + return False + + def collide_list_all_circle(self, list_circle): + ''' Checks if this circle intersects with all of the circles in circle_list. ''' + for circle in list_circle: + if not collide_circle(self, circle): + return False + else: # else of the for loop + return True + + def collide_rect(self, another_rect): + ''' Checks if this circle collides with another_rect. ''' + return not ((self.center_x + self.radius) < another_rect.left or (self.center_x - self.radius) > another_rect.right or (self.center_y + self.radius) < another_rect.top or (self.center_y - self.radius) > another_rect.bottom) + + def collide_list_rect(self, list_rect): + ''' Returns True if collides with any of the rect in the list. ''' + for temp_rect in list_rect: + if collide_rect(self, temp_rect): + return True + else: + return False + + def collide_list_all_rect(self, list_rect): + ''' Returns True if collides with all the rect in the list. ''' + for temp_rect in list_rect: + if not collide_rect(self, temp_rect): + return False + else: + return True + + @classmethod + # create a circle with same center and radius=sqrt(w^2+h^2) + def get_circumcircle_from_rect(cls, another_rect): + import math + return cls(another_rect.center, math.sqrt(another_rect.width ** 2 + another_rect.height ** 2) / 2) + + @classmethod + # radius of incircle is same as the min of width and height (as it's not a + # square) + def get_incircle_from_rect(cls, another_rect): + import math + return cls(another_rect.center, min(another_rect.width, another_rect.height)) + + # union -needs to be a circle *within* which the union of the bounding box of all the circles can be fit. + # warning about all the union functions: The circles returned are not + # necessarily the most optimum union circle, but it will surely contain + # the others. + def _union(self, circle_1, circle_2): + ''' self.__union((new_center_x,new_center_y),new_radius) -> ([x,y],radius) which are the resultant values. ''' + return self.circumcircle_from_rect(circle_1.bbox.union(circle_2.bbox)) + + def union(self, new_circle): + ''' Returns a new circle which is the union of these two circles ''' + return self._union(self, new_circle) + + def union_ip(self, new_circle): + ''' Turns this circle into a circle which is a union of these two circles. ''' + temp_circle = self._union(self, new_circle) + self.center, self.radius = temp_circle.center, temp_circle.radius + + # list_circle_params : [((x1,y1),radius_1), ((x2,y2),radius_2), ...] + def _unionall(self, circle_1, seq_circle): + ''' returns ([x,y],radius) for the resultant union of all circles. + sequence_circle must be a sequence of circles. ''' + return self.circumcircle_from_rect(circle_1.bbox.unionall([temp_circle.bbox for temp_circle in seq_circle])) + + def unionall(self, seq_circle): # seq_circle: sequence of circles + return self._unionall(self, seq_circle) + + def unionall_ip(self, seq_circle): + ''' Turns this circle into a circle which is a union of this circles with all circles in seq_circle''' + temp_circle = self._unionall(self, seq_circle) + self.center, self.radius = temp_circle.center, temp_circle.radius + + # draw functions + def draw(self, surface, (x, y)=(None, None), color=None, width=1, use_antialiasing=False): + ''' Draws this Circle on surface. It must be of width and height greater than or equal to its bbox. + (x,y)->co-ordinates whether the center of the Circle should be drawn on the surface. + width argument doesnt work with antialiasing. width=0 makes it filled. + antialiasing may break in later versions as the backend pygame.gfxdraw is experimental. + color falls back first to Circle.color (if present) and then to blue color.''' + if (x, y) == (None, None): + (x, y) = self.center + if not color: + if self.color: + color = self.color + else: + color = pygame.Color("blue") + + if not use_antialiasing: + pygame.draw.circle(surface, color, (x, y), self.radius, width) + else: + pygame.gfxdraw.aacircle(surface, x, y, self.radius, color) + + def get_surface(self, surface=None, flags=0): + ''' Returns a new surface with dimensions of (Circle.bbox.width,Circle.bbox.width) and other properties as surface. + This Circle can be safely drawn on it.''' + if surface: + return pygame.Surface((self.bbox.width, self.bbox.height), flags, surface) + else: + return pygame.Surface((self.bbox.width, self.bbox.height), flags) + + def get_surface_drawn(self, (x, y)=(None, None), color=None, width=1, use_antialiasing=False, surface=None, flags=0): + ''' Returns a surface which has the tiltedRect drwan on it. ''' + temp_surf = self.get_surface(surface, flags) + self.draw(temp_surf, (x, y), color, width, use_antialiasing) + return temp_surf + + # end of Circle class + # start of tiltedRect class + + +class tiltedRect(pygame.Rect): + # make a metaclass which wraps a lot of functions just like move is defined below. + # functions to be wrapped - move,inflate + # Think about them - clamp,clip,union,fit,contains,collide + # the metaclass can probably also change the __str__ of the class. + # About drawing - use pygame.transform.rotate to get a rotated surface to + # be drawed. + + def __init__(self, (left, top), (width, height), angle, centered_coords=False, color=None): + ''' Angle in degrees. + centered_coords -> whether the (left,top) is actually intended to be (center_x,center_y) or not. + I.e. if centered_coords==True the (left,top) co-ordinates are used as (center_x,center_y) co-ordinates of the tiltedRect''' + self.angle = angle + self.color = color + if centered_coords: + left = left - width / 2 + top = top - height / 2 + super(tiltedRect, self).__init__((left, top), (width, height)) + + def __copy__(self): + return tiltedRect((self.left, self.top), (self.width, self.height), self.angle) + + def __repr__(self): + return "{0.__class__.__name__}((top={0.top}, left={0.left}), (width={0.width}, height={0.height}), angle={0.angle}, color={0.color})".format(self) + + @classmethod + def create_from_rect(cls, another_rect, angle): + return cls((another_rect.left, another_rect.top), (another_rect.width, another_rect.height), angle) + + def get_rect(self): + return pygame.Rect((left, top), (width, height)) + + def move(self, x, y): + return self.create_from_rect(super(tiltedRect, self).move(x, y), self.angle) + + def inflate(self, x, y): + return self.create_from_rect(super(tiltedRect, self).inflate(x, y), self.angle) + + def normalize(self): # not sure what this one does - ask others + return self.create_from_rect(super(tiltedRect, self).normalize(), self.angle) + + def rotate(self, some_angle): + temp_return = self.__copy__() + temp_return.angle += some_angle + return temp_return + + def rotate_ip(self, some_angle): + self.angle += some_angle + + # generalise the parameters to accept x1,y1,x2,y2,rot_angle + def rotate_point_relative(self, x, y, theta=None): + ''' x,y -> Absolute co-ords of the point to be rotated wrt to the rectangles center. self.centerx,self.centery are internally substracted from them. + theta -> angle (in degrees) by which the point is to be rotated. Default value is self.angle. + Returns absolute positions of the points after rotation. ''' + if theta is None: + theta = self.angle + return self.rotate_point(x, y, self.centerx, self.centery, theta) + + @classmethod + # to be allowed as rotate_point(class) + def rotate_point(cls, x, y, origin_x, origin_y, theta): + ''' x,y -> Absolute co-ords of the point to be rotated. origin_x,origin_y are internally substracted from them. + (origin_x,origin_y) -> Absolute co-ords of the origin wrt to which the point will be rotated + theta -> angle (in degrees) by which the point is to be rotated. + Returns absolute positions of the points after rotation. ''' + import math + x_diff = x - origin_x + y_diff = y - origin_y + # -y_diff because of the inverted nature of y co-ordinate system in pygame + y_diff = -y_diff + r = math.sqrt(x_diff ** 2 + y_diff ** 2) + initial_angle = math.atan2(y_diff, x_diff) + final_angle = initial_angle + math.radians(theta) + rotated_x_diff = r * math.cos(final_angle) + rotated_y_diff = r * math.sin(final_angle) + rotated_y_diff = -rotated_y_diff # - used for same reason as above + rotated_x = rotated_x_diff + origin_x + rotated_y = rotated_y_diff + origin_y + return (rotated_x, rotated_y) + + def rotate_point_polar(self, r, initial_angle, theta=None): + ''' theta and initial_angle in radians. + initial_angle ->angle r makes with horizontal. + theta -> angle to be rotated by. ''' + if theta is None: + theta = self.angle + final_angle = initial_angle + theta + rotated_x = r * math.cos(final_angle) + rotated_y = r * math.sin(final_angle) + return (rotated_x, rotated_y) + + @property + def bottomleft_rotated(self): + temp = self.bottomleft + return self.rotate_point_relative(temp[0], temp[1]) + + @property + def topleft_rotated(self): + temp = self.topleft + return self.rotate_point_relative(temp[0], temp[1]) + + @property + def bottomright_rotated(self): + temp = self.bottomright + return self.rotate_point_relative(temp[0], temp[1]) + + @property + def topright_rotated(self): + temp = self.topright + return self.rotate_point_relative(temp[0], temp[1]) + + @property + def midleft_rotated(self): + temp = self.midleft + return self.rotate_point_relative(temp[0], temp[1]) + + @property + def midright_rotated(self): + temp = self.midright + return self.rotate_point_relative(temp[0], temp[1]) + + @property + def midtop_rotated(self): + temp = self.midtop + return self.rotate_point_relative(temp[0], temp[1]) + + @property + def midbottom_rotated(self): + temp = self.midbottom + return self.rotate_point_relative(temp[0], temp[1]) + + # related to bounding box + @property + def bbox(self): + list_x = [self.topleft_rotated[0], self.topright_rotated[0], + self.bottomleft_rotated[0], self.bottomright_rotated[0]] + list_y = [self.topleft_rotated[1], self.topright_rotated[1], + self.bottomleft_rotated[1], self.bottomright_rotated[1]] + min_x = floor(min(list_x)) + max_x = ceil(max(list_x)) + min_y = floor(min(list_y)) + max_y = floor(max(list_y)) + return pygame.Rect((min_x, min_y), (max_x - min_x, max_y - min_y)) + # corners + + @property + def corners(self): + ''' List of actual (rotated) corners. ''' + return [self.topleft_rotated, self.topright_rotated, self.bottomright_rotated, self.bottomleft_rotated] + + @property + def corners_relative(self): + return [(temp[0] - self.centerx, temp[1] - self.centery) for temp in self.corners] + + # circles related to the rect + def get_circumcircle(self): + Circle.get_circumcircle_from_rect(self.bbox) + + def get_incircle(self): + Circle.get_incircle_from_rect(self.bbox) + + # collision events + def collide_point(self, *args): + ''' collidepoint(x,y) -> + collidepoint((x,y)) -> ''' + temp_len = len(args) + temp_x = None + temp_y = None + if temp_len == 2: + temp_x = args[0] + temp_y = args[1] + elif temp_len == 1: + if len(args[0]) == 2: + temp_x, temp_y = args[0] + else: + raise TypeError("Arguments are of incorrect type") + else: + raise TypeError("Arguments are of incorrect type") + if temp_x is None or temp_y is None: + raise TypeError("argument must contain two numbers") + return pygame.Rect((self.left, self.top), (self.width, self.height)).collidepoint(self.rotate_point_relative(temp_x, temp_y)) + + def collide_rect(self, rect): + ''' Tests if it collides with rect, which should be instance of rect class. ''' + # First test:whether bbox collides with rect. If not, no collision. + if not self.bbox.colliderect(rect): + return False + else: + # Todo: Else sure test: Need to do line class first. Test if any line + # of titedRect collides with any line of (instead, the whole) rect. + pass + + # draw functions + def draw(self, surface, (x, y)=(None, None), color=None, width=1, use_antialiasing=False, blend=True): + ''' Draws this tiltedRect on surf_to_draw_on. It must be of width and height greater or equal to its bbox. + (x,y)->co-ordinates whether the lefttop point of its bbox should be drawn on the surface. + width argument doesnt work with antialiasing. width=0 makes it filled. + blend argument works only with antialiasing. The boolean blend argument set to true will blend the shades with existing shades instead of overwriting them. + color falls back first to tiltedRect.color (if present) and then to blue color.''' + if (x, y) == (None, None): + (x, y) = (0, 0) + if not color: + if self.color: + color = self.color + else: + color = pygame.Color("blue") + iter_corners = [(temp[0] + x, temp[1] + y) + for temp in self.corners_relative] + if not use_antialiasing: + pygame.draw.polygon(surface, color, iter_corners, width) + else: + pygame.draw.aalines(surface, color, True, iter_corners, blend) + + def get_surface(self, surface=None, flags=0): + ''' Returns a new surface with dimensions of (tiltedRect.bbox.width,tiltedRect.bbox.width) and other properties as surface. + This tiltedRect can be safely drawn on it.''' + if surface: + return pygame.Surface((self.bbox.width, self.bbox.height), flags, surface) + else: + return pygame.Surface((self.bbox.width, self.bbox.height), flags) + + def get_surface_drawn(self, (x, y)=(None, None), color=None, width=1, use_antialiasing=False, blend=True, surface=None, flags=0): + ''' Returns a surface which has the tiltedRect drwan on it. ''' + temp_surf = self.get_surface(surface, flags) + self.draw(temp_surf, (x, y), color, width, use_antialiasing, blend) + return temp_surf + + def collide_tiltedRect(self, another_tiltedRect): + '''Checks whether this tiltedRect collides with another_titltedrect, which must be a instance of titledRect. ''' + # Rotates both tiltedRect by -another_tiltedRect.angle. So, another_tiltedRect becomes a Rect called temp_Rect. + # Then check if the rotated version of this tiltedRect + # (temp_tiltedRect) collides with the obtained Rect. + temp_tiltedRect = self.rotate(-another_tiltedRect.angle) + temp_Rect = another_tiltedRect.get_rect() + return temp_tiltedRect.colliderect(temp_Rect) + +# functions outside all classes + + +# list_to_get is attached to a constant list here +def get_event_list(event_types=None, further_check_variable_name=None, further_check_value=None, list_to_get=None): + ''' Get the required events from the event_list. + get_event_list([event_types],[further_check_variable_name],further_check_value=None,list_to_get=fun_Game.list_event): + event_types (optional): event_name|list[event_name] + get_event_list() -> returns whole event_list(to be precise, list_to_get) + get_event_list(events) -> returns event_list(to be precise, list_to_get) filtered to contain only "events" + get_event_list([events],further_check_variable_name="var_name",further_check_value=value) -> + returns sub-list containing those elements of get_event_list([events]) for which var_name=value + Potential candidates for list_to_get -> fun_Game.list_event, fun_Game.list_event_old, fun_Game.list_state_all_buttons, fun_Game.list_state_all_buttons_old''' + if list_to_get is None: + list_to_get = fun_Game.list_event_old + if further_check_variable_name: + further_check_enabled = True + else: + further_check_enabled = False + if event_types: + if not hasattr(event_types, "__iter__"): + # if not iterable, make a list out of it + event_types = [event_types] + + def func_filter(): + list_return = [] + if not event_types: + return list_to_get + # all events in fun_Game.list_event have a "type" property + for temp_1 in list_to_get: + for temp_2 in event_types: + if temp_1.type == temp_2: + list_return.append(temp_1) + return list_return + + # returns part of temp_list which passes further_check + def further_check_filter(temp_list): + list_return = [] + if further_check_enabled: + for temp in temp_list: + # may raise error if further_check_variable_name is not present + # for all members of the list + if getattr(temp, further_check_variable_name) == further_check_value: + list_return.append(temp) + else: + list_return = temp_list + return list_return + # returns elements common in fun_Game.list_events and events which are + # accepted after further_check + return further_check_filter(func_filter()) +# end of get_event_list() + + +# Maybe make a non-square rooted function for comparison (faster) +def distance((x1, y1), (x2, y2)): + import math + return(math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)) + +# following constants are required for the next function +UNION = 1 +INTERSECTION = 2 +DIFFERENCE = 3 +ADD = 4 +# for each member (usually objects) in the list, it is replaced by one of +# its variables +MAKE_IT_VARIABLE_LIST = 5 + + +def operation_on_lists(operation, list1, list2=None, variable_name=None): + ''' operation_on_lists(operation, list1, list2) -> list + Does operation involving list1 and list2. + variable_name is required only for MAKE_IT_VARIABLE_LIST operation. It is a string representing the variable name + Valid operations are UNION, INTERSECTION, DIFFERENCE, ADD, MAKE_IT_VARIABLE_list ''' + def intersection(): + if list2 is None: + raise TypeError("second argument i.e. list2 should be a list") + list_return = [] + for temp1 in list1: + for temp2 in list2: + if temp1 == temp2: + list_return.append(temp1) + return list_return + + def union(): + if list2 is None: + raise TypeError("second argument i.e. list2 should be a list") + list_return = uniquelist() + list_return.extend(list1) + list_return.extend(list2) + return list_return + + def add(): + if list2 is None: + raise TypeError("second argument i.e. list2 should be a list") + list_return = [] + list_return.append(list1) + list_return.append(list2) + return list_return + + def difference(): + if list2 is None: + raise TypeError("second argument i.e. list2 should be a list") + list_return = [] + list_intersection = intersection() + for temp1 in list1: + if temp1 not in list_intersection: + list_return.append(temp1) + return list_return + + # for each member (usually objects) in the list, it is replaced by one of + # its variables + def make_it_variable_list(): + if variable_name is None: + raise TypeError("variable_name should be a string") + if not list2 is None: + raise TypeError("list2 should be None") + return_list = [getattr(temp, variable_name) for temp in list1] + + if operation == UNION: + return union() + elif operation == INTERSECTION: + return intersection() + elif operation == DIFFERENCE: + return difference() + elif operation == ADD: + return add() + elif operation == MAKE_IT_VARIABLE_LIST: + return make_it_variable_list() + else: + raise ValueError( + "Invalid operation name. Valid operations are UNION, INTERSECTION, DIFFERENCE.") +# end of operation_on_lists() + +# Todo: see below +# 1. Allow rect-like and circle-like constructs wherever rect and circle are expected. +# 2. Do all sort of type-checking. +# 3.Use sprites and groups in pygame as containers +# Imp: Allow debugging with event handlers and others with @debug + +# Design tips I will use later +# IMP: always derive classes from object. Else, in python 2.x, they become old-style class which sucks. +# use tuples instead of lists wherver possible because they are usually faster to create +# learn about generators +# named tuples, arrays are better. use more of them. named tuples can be accessed. +# refactor code into proper modules as-well-as use better named functions and variables +# use enumerate() for getting the index and value of elements while looping through lists. This mistake done atleast 1 time. +# use xrange instead of range. It produces one at a time. range is replaced by xrange in python 3.x +# use reversed() for backward loops, sorted() for sorted order. +# instead of zip(), use izip(). +# use key instead of comparator +# use iter(iter_type,sentinel_value) instead of for loops +# partial() makes a function with more number of argument to less number of argument +# use for/else instead of exit based on flags +# look at dict.iteritems() and dict.setdefaults +# learn defaultdicts and collections module diff --git a/basic.py b/basic.py new file mode 100644 index 0000000..7370e25 --- /dev/null +++ b/basic.py @@ -0,0 +1,23 @@ +import inspect + +def original_decorator(func): + # need to access class here + # for eg, to append the func itself to class variable "a", to register itself + # or say, append their default arg values to class variable "a" + return func + +class A(object): + a=[] + + @classmethod + @original_decorator + def some_method(self,a=5): + ''' hello''' + print "Calling some_method" + @original_decorator + def some_method_2(self): + ''' hello again''' + print "Calling some_method_2" + +##print("A.a is %s"%A.a) +##print(inspect.getargspec(A.some_method)) \ No newline at end of file diff --git a/bug.py b/bug.py new file mode 100644 index 0000000..12e6af3 --- /dev/null +++ b/bug.py @@ -0,0 +1,38 @@ +from functools import partial +import inspect + +class meta(type): + def __new__(cls, name, base, clsdict): + temp_cls = type.__new__(cls, name, base, clsdict) + methods=inspect.getmembers(temp_cls,inspect.ismethod) + print methods + print "inspecting.." + for tmp in methods: + method=tmp[1] + tmp_spec=inspect.getargspec(method) + if "main_func" in tmp_spec.args: + main_func=tmp_spec.defaults[1] + tmp_spec.defaults[0]() + main_func(50) + f=method.im_func + print f + f.func_code,f.func_defaults,f.func_dict,f.func_doc,f.func_name=main_func.func_code, main_func.func_defaults,main_func.func_dict,main_func.func_doc,main_func.func_name + return temp_cls + +def doit(what_to_do, main_func): + def asd(what_to_do=what_to_do,main_func=main_func): + pass + return asd + +def pr5(): + print 5 + +do_pr5=partial(doit,pr5) + +class A(object): + __metaclass__=meta + a=[] + @do_pr5 + def abc(self,a=5): + ''' hello''' + print a+1 diff --git a/bug_standalone.py b/bug_standalone.py new file mode 100644 index 0000000..36e5f0b --- /dev/null +++ b/bug_standalone.py @@ -0,0 +1,27 @@ +import pygame +w,h=800,200 +fps=60 +pygame.init() +screen = pygame.display.set_mode([w, h]) +color=pygame.Color("white") +clock=pygame.time.Clock() +radius=40 +x,y=800,100 +def get_bbox(x,y): + left = x - radius + top = y - radius + width = radius * 2 + height = radius * 2 + return pygame.Rect((left, top), (width, height)) + +while True: + pygame.event.pump() + old_x=x + x-=5 + screen.fill(pygame.Color("black"),get_bbox(old_x,y)) + pygame.draw.circle(screen, color, (x, y), radius, 1) + pygame.display.update([get_bbox(x,y),get_bbox(old_x,y)]) +## pygame.display.flip() + clock.tick(fps) +## time_passed=clock.get_time() +## print(time_passed) diff --git a/event.py b/event.py new file mode 100644 index 0000000..e780202 --- /dev/null +++ b/event.py @@ -0,0 +1,517 @@ +import pygame +import pygame.locals as pygame_consts +from UncasedDict import UncasedDict + +DICT_EVENT_IB = { + "QUIT": pygame_consts.QUIT, + "ACTIVE_EVENT": pygame_consts.ACTIVEEVENT, + "KEY_DOWN": pygame_consts.KEYDOWN, + "KEY_UP": pygame_consts.KEYUP, + "MOUSE_MOTION": pygame_consts.MOUSEMOTION, + "MOUSE_BUTTON_UP": pygame_consts.MOUSEBUTTONUP, + "MOUSE_BUTTON_DOWN": pygame_consts.MOUSEBUTTONDOWN, + "JOY_AXIS_MOTION": pygame_consts.JOYAXISMOTION, + "JOY_BALL_MOTION": pygame_consts.JOYBALLMOTION, + "JOY_HAT_MOTION": pygame_consts.JOYHATMOTION, + "JOY_BUTTON_UP": pygame_consts.JOYBUTTONUP, + "JOY_BUTTOND_OWN": pygame_consts.JOYBUTTONDOWN, + "VIDEO_RESIZE": pygame_consts.VIDEORESIZE, + "VIDEO_EXPOSE": pygame_consts.VIDEOEXPOSE, + # "USER_EVENT": pygame_consts.USEREVENT, + # think of begin_step, step and end_step +} + + +DICT_KEYS = { + r"BACKSPACE": pygame_consts.K_BACKSPACE, + r"TAB": pygame_consts.K_TAB, + r"CLEAR": pygame_consts.K_CLEAR, + r"RETURN": pygame_consts.K_RETURN, + r"PAUSE": pygame_consts.K_PAUSE, + r"ESCAPE": pygame_consts.K_ESCAPE, + r"SPACE": pygame_consts.K_SPACE, + r"EXCLAIM": pygame_consts.K_EXCLAIM, + r"QUOTEDBL": pygame_consts.K_QUOTEDBL, + r"HASH": pygame_consts.K_HASH, + r"DOLLAR": pygame_consts.K_DOLLAR, + r"AMPERSAND": pygame_consts.K_AMPERSAND, + r"QUOTE": pygame_consts.K_QUOTE, + r"LEFTPAREN": pygame_consts.K_LEFTPAREN, + r"RIGHTPAREN": pygame_consts.K_RIGHTPAREN, + r"ASTERISK": pygame_consts.K_ASTERISK, + r"PLUS": pygame_consts.K_PLUS, + r"COMMA": pygame_consts.K_COMMA, + r"MINUS": pygame_consts.K_MINUS, + r"PERIOD": pygame_consts.K_PERIOD, + r"SLASH": pygame_consts.K_SLASH, + r"0": pygame_consts.K_0, + r"1": pygame_consts.K_1, + r"2": pygame_consts.K_2, + r"3": pygame_consts.K_3, + r"4": pygame_consts.K_4, + r"5": pygame_consts.K_5, + r"6": pygame_consts.K_6, + r"7": pygame_consts.K_7, + r"8": pygame_consts.K_8, + r"9": pygame_consts.K_9, + r"COLON": pygame_consts.K_COLON, + r"SEMICOLON": pygame_consts.K_SEMICOLON, + r"LESS": pygame_consts.K_LESS, + r"EQUALS": pygame_consts.K_EQUALS, + r"GREATER": pygame_consts.K_GREATER, + r"QUESTION": pygame_consts.K_QUESTION, + r"AT": pygame_consts.K_AT, + r"LEFTBRACKET": pygame_consts.K_LEFTBRACKET, + r"BACKSLASH": pygame_consts.K_BACKSLASH, + r"RIGHTBRACKET": pygame_consts.K_RIGHTBRACKET, + r"CARET": pygame_consts.K_CARET, + r"UNDERSCORE": pygame_consts.K_UNDERSCORE, + r"BACKQUOTE": pygame_consts.K_BACKQUOTE, + r"a": pygame_consts.K_a, + r"b": pygame_consts.K_b, + r"c": pygame_consts.K_c, + r"d": pygame_consts.K_d, + r"e": pygame_consts.K_e, + r"f": pygame_consts.K_f, + r"g": pygame_consts.K_g, + r"h": pygame_consts.K_h, + r"i": pygame_consts.K_i, + r"j": pygame_consts.K_j, + r"k": pygame_consts.K_k, + r"l": pygame_consts.K_l, + r"m": pygame_consts.K_m, + r"n": pygame_consts.K_n, + r"o": pygame_consts.K_o, + r"p": pygame_consts.K_p, + r"q": pygame_consts.K_q, + r"r": pygame_consts.K_r, + r"s": pygame_consts.K_s, + r"t": pygame_consts.K_t, + r"u": pygame_consts.K_u, + r"v": pygame_consts.K_v, + r"w": pygame_consts.K_w, + r"x": pygame_consts.K_x, + r"y": pygame_consts.K_y, + r"z": pygame_consts.K_z, + r"DELETE": pygame_consts.K_DELETE, + r"KP0": pygame_consts.K_KP0, + r"KP1": pygame_consts.K_KP1, + r"KP2": pygame_consts.K_KP2, + r"KP3": pygame_consts.K_KP3, + r"KP4": pygame_consts.K_KP4, + r"KP5": pygame_consts.K_KP5, + r"KP6": pygame_consts.K_KP6, + r"KP7": pygame_consts.K_KP7, + r"KP8": pygame_consts.K_KP8, + r"KP9": pygame_consts.K_KP9, + r"KP_PERIOD": pygame_consts.K_KP_PERIOD, + r"KP_DIVIDE": pygame_consts.K_KP_DIVIDE, + r"KP_MULTIPLY": pygame_consts.K_KP_MULTIPLY, + r"KP_MINUS": pygame_consts.K_KP_MINUS, + r"KP_PLUS": pygame_consts.K_KP_PLUS, + r"KP_ENTER": pygame_consts.K_KP_ENTER, + r"KP_EQUALS": pygame_consts.K_KP_EQUALS, + r"UP": pygame_consts.K_UP, + r"DOWN": pygame_consts.K_DOWN, + r"RIGHT": pygame_consts.K_RIGHT, + r"LEFT": pygame_consts.K_LEFT, + r"INSERT": pygame_consts.K_INSERT, + r"HOME": pygame_consts.K_HOME, + r"END": pygame_consts.K_END, + r"PAGEUP": pygame_consts.K_PAGEUP, + r"PAGEDOWN": pygame_consts.K_PAGEDOWN, + r"F1": pygame_consts.K_F1, + r"F2": pygame_consts.K_F2, + r"F3": pygame_consts.K_F3, + r"F4": pygame_consts.K_F4, + r"F5": pygame_consts.K_F5, + r"F6": pygame_consts.K_F6, + r"F7": pygame_consts.K_F7, + r"F8": pygame_consts.K_F8, + r"F9": pygame_consts.K_F9, + r"F10": pygame_consts.K_F10, + r"F11": pygame_consts.K_F11, + r"F12": pygame_consts.K_F12, + r"F13": pygame_consts.K_F13, + r"F14": pygame_consts.K_F14, + r"F15": pygame_consts.K_F15, + r"NUMLOCK": pygame_consts.K_NUMLOCK, + r"CAPSLOCK": pygame_consts.K_CAPSLOCK, + r"SCROLLOCK": pygame_consts.K_SCROLLOCK, + r"RSHIFT": pygame_consts.K_RSHIFT, + r"LSHIFT": pygame_consts.K_LSHIFT, + r"RCTRL": pygame_consts.K_RCTRL, + r"LCTRL": pygame_consts.K_LCTRL, + r"RALT": pygame_consts.K_RALT, + r"LALT": pygame_consts.K_LALT, + r"RMETA": pygame_consts.K_RMETA, + r"LMETA": pygame_consts.K_LMETA, + r"LSUPER": pygame_consts.K_LSUPER, + r"RSUPER": pygame_consts.K_RSUPER, + r"MODE": pygame_consts.K_MODE, + r"HELP": pygame_consts.K_HELP, + r"PRINT": pygame_consts.K_PRINT, + r"SYSREQ": pygame_consts.K_SYSREQ, + r"BREAK": pygame_consts.K_BREAK, + r"MENU": pygame_consts.K_MENU, + r"POWER": pygame_consts.K_POWER, + r"EURO": pygame_consts.K_EURO, +} + +LIST_EVENTS_GAME_PRE = ["BEGIN_STEP"] +# Custom events to be registered here +LIST_EVENTS_GAME_POST = ["STEP", "DRAW", "END_STEP"] +LIST_EVENTS_CUSTOM = [ + "KEY_PRESS", "MOUSE_PRESS", "Instance_Create", "Instance_Destroy"] + + +dict_event_name = UncasedDict() +list_event = [] +list_event_old = [] +list_state_key = [] +list_state_key_old = [] +list_state_mouse, list_state_mouse_old = [], [] + + +def register_event_name(ev_name, ev_id=None): + if (ev_name in dict_event_name): + raise TypeError( + "ev_name={0} is already registered".format(ev_name)) + if (ev_id is None): + ev_id = max( + pygame_consts.USEREVENT + 1, max(dict_event_name.values()) + 1) + dict_event_name[ev_name] = ev_id + + +def add_each_event(ev): + if not (ev.ev_name in dict_event_name): + return TypeError("ev_name not registered") + else: + # make list_event_accessible + list_event.append(ev) + + +def add_returned_events(ev_or_ev_list): + # fix below cond + if hasattr(ev_or_ev_list, "__iter__"): + # list of events + for each_ev in ev_or_ev_list: + add_each_event(each_ev) + else: + # single event + add_each_event(ev_or_ev_list) + + +def get_event_name(ev_id): + for ev_name in dict_event_name: + if dict_event_name[ev_name] == ev_id: + return ev_name + + +def get_event_id(ev_name): + return dict_event_name.get(ev_name) + + +def get_key_name(key_id): + for key_name in DICT_KEYS: + if DICT_KEYS[key_name] == key_id: + return key_name + + +def get_key_id(key_name): + return DICT_KEYS.get(key_name) + + +def get_mouse_btn_name(btn_id): + # btn_id can be 0, 1 or 2 + if btn_id == 0: + return "left" + elif btn_id == 1: + return "middle" + elif btn_id == 2: + return "right" + else: + raise ValueError("btn_id must be 0, 1 or 2") + +# No sort_events, event_generators should be pushed in correct order + + +def fuzzy_match_event_name(string_, event_or_action): + str_upper = string_.upper() + if not (str_upper in dict_event_name): + event_or_action = event_or_action.lower() + if event_or_action == "event" or event_or_action == "action": + # STR_STARTER becomes event_ or action_ + STR_STARTER = event_or_action.upper() + "_" + else: + raise TypeError( + "event_or_action arg must be 'event' or 'action'.") + str_normalized = str_upper[ + str_upper.find(STR_STARTER) + len(STR_STARTER):] + for tmp_event_name in dict_event_name: + # tmp event is the event_name, keys of KW_EVENTS + tmp_event_name_normalized = tmp_event_name.upper().replace("_", "") + if str_normalized.startswith(tmp_event_name) or str_normalized.startswith(tmp_event_name_normalized): + return tmp_event_name + else: + return str_upper + + +def add_ev_name_GAME_PRE(): + for ev_name in LIST_EVENTS_GAME_PRE: + register_event_name(ev_name) + + +def add_ev_name_IB(): + for ev_name in DICT_EVENT_IB: + register_event_name(ev_name, DICT_EVENT_IB[ev_name]) + + +def add_ev_name_GAME_POST(): + for ev_name in LIST_EVENTS_GAME_POST: + register_event_name(ev_name) + + +def add_ev_name_CUSTOM(): + for ev_name in LIST_EVENTS_CUSTOM: + register_event_name(ev_name) + +# event_generators + + +def gen_event_GAME_PRE(): + return [Event(ev_name) for ev_name in LIST_EVENTS_GAME_PRE] + + +def gen_event_IB(): + # move current list to old list + list_event_IB = pygame.event.get() + # Above list contains pygame_events, cant be modified + # But need to replace type with ev_name + new_list_ev = [] + for each_ev in list_event_IB: + ev_name = get_event_name(each_ev.type) + tmp_ev = Event(ev_name, each_ev.dict) + new_list_ev.append(tmp_ev) + + return new_list_ev + + +def gen_event_GAME_POST(): + return [Event(ev_name) for ev_name in LIST_EVENTS_GAME_POST] + + +def gen_event_KEY_PRESS(): + global list_state_key, list_state_key_old + list_state_key_old = list_state_key + list_state_key = pygame.key.get_pressed() + list_key_press_event = [] + for tmp_key_id, bool_pressed in enumerate(list_state_key): + if bool_pressed: + tmp_key_name = get_key_name(tmp_key_id) + list_key_press_event.append( + Event("KEY_PRESS", {"key_name": tmp_key_name.lower(), "key_id": tmp_key_id})) + return list_key_press_event + + +def gen_event_MOUSE_PRESS(): + global list_state_mouse, list_state_mouse_old + list_state_mouse_old = list_state_mouse + list_state_mouse = pygame.mouse.get_pressed() + list_mouse_press_event = [] + for tmp_mouse_btn_id, bool_pressed in enumerate(list_state_mouse): + if bool_pressed: + tmp_btn_name = get_mouse_btn_name(tmp_mouse_btn_id) + list_mouse_press_event.append( + Event("MOUSE_PRESS", {"btn_name": tmp_btn_name.lower(), "btn_id": tmp_mouse_btn_id})) + return list_mouse_press_event + +# pygame.mouse.get_pressed + +# add ev_names +# Ordered list of name adders +LIST_ADDER_EV_NAME = [ + add_ev_name_IB, add_ev_name_GAME_PRE, add_ev_name_GAME_POST, add_ev_name_CUSTOM] +for each_adder_func in LIST_ADDER_EV_NAME: + each_adder_func() + + +# add event generators +# Ordered list of generators +list_ordered_custom_generator = [gen_event_KEY_PRESS, gen_event_MOUSE_PRESS] + +list_ordered_generators = [ + gen_event_GAME_PRE, gen_event_IB, list_ordered_custom_generator, gen_event_GAME_POST] + + +def register_custom_event_generator(ev_func): + ''' ev_func will be run in each step before step event and after begin step event and should return a / a list of dicts + each with ev_name (to be registered beforehand) and other props''' + global list_ordered_custom_generator + list_ordered_custom_generator.append(ev_func) + + +def get_event_generators(): + return_list = [] + for gen_or_list in list_ordered_generators: + if hasattr(gen_or_list, "__iter__"): + return_list.extend(gen_or_list) + else: + return_list.append(gen_or_list) + return return_list + + +# Todo: Howto programatically register at the right position of generator list +# Event collector + + +def collect_events(): + # First, clear the old ev list + global list_event, list_event_old + list_event_old = list_event + list_event = [] + for each_gen in get_event_generators(): + add_returned_events(each_gen()) + +# Event dispatcher +# To be used by appr. classes + + +def dispatch_events_class(cls): + # cls.list_class must be available + for each_class in cls.list_class: + each_class.dispatch_events_instance(list_event) + + +def dispatch_events_instance(cls, list_event): + for each_instance in cls.list_instance: + each_instance.dispatch_events_self(list_event) + + +def dispatch_events_self(self, list_event): + for each_ev in list_event: + if each_ev.ev_name.upper() in self.dict_action_func: + self.dispatch_events_local() + for each_action in self.dict_action_func[each_ev.ev_name]: + # similar as self.action_step(each_ev) + each_action(self, each_ev) + + +def dispatch_events_local(self): + for each_ev in self.list_event_local: + if each_ev.ev_name.upper() in self.dict_action_func: + for each_action in self.dict_action_func[each_ev.ev_name]: + # similar as self.action_step(each_ev) + each_action(self, each_ev) + self.clear_event_local() + + +def push_event_local(self, ev_or_ev_list): + if hasattr(ev_or_ev_list, "__iter__"): + # list of events + for each_ev in ev_or_ev_list: + self._push_event_local_single(each_ev) + else: + # single event + self._push_event_local_single(ev_or_ev_list) + + +def clear_event_local(self): + self.list_event_local = [] + + +def _push_event_local_single(self, ev): + self.list_event_local.append(ev) + + +class Event(object): + + def __init__(self, ev_name, dict_=None): + super(Event, self).__init__() + self.ev_name = ev_name + self.dict = dict_ + + +def _check_event(ev_name, new_or_old): + ''' ev_name_or_type => name_or_type(numb)_of_event + new_or_old => current_event_list or old_event_list ''' + global list_event, list_event_old + matched_events = [] + if new_or_old == "new": + list_to_check = list_event + elif new_or_old == "old": + list_to_check = list_event_old + else: + raise AttributeError("new_or_old must be 'new' or 'old'.") + + ev_name = fuzzy_match_event_name(ev_name, "event") + + for ev in list_to_check: + if ev.ev_name == ev_name: + matched_events.append(ev) + return matched_events + + +def check_event(ev_name): + return _check_event(ev_name, "new") + + +def check_event_old(ev_name): + return _check_event(ev_name, "old") + + +def _get_key_press(key_name=None, new_or_old=None): + ''' Returns list of dict with key_name and key_id, if key_name matches. + If key_name is None, all key preeses are allowed.''' + global list_state_key, list_state_key_old + if new_or_old == "new": + list_to_check = list_state_key + elif new_or_old == "old": + list_to_check = list_state_key_old + else: + raise TypeError("new_or_old must be 'new' or 'old', CANT be None") + list_key_press_event = [] + for tmp_key_id, bool_pressed in enumerate(list_to_check): + if bool_pressed: + tmp_key_name = get_key_name(tmp_key_id) + if (key_name is None) or (tmp_key_name.lower() == key_name.lower()): + # Todo: Cant match event_name directly, need a fuzzy key name + # search func + list_key_press_event.append( + {"key_name": tmp_key_name.lower(), "key_id": tmp_key_id}) + return list_key_press_event + + +def _get_mouse_press(btn_name=None, new_or_old=None): + global list_state_mouse, list_state_mouse_old + if new_or_old == "new": + list_to_check = list_state_mouse + elif new_or_old == "old": + list_to_check = list_state_mouse_old + else: + raise TypeError("new_or_old must be 'new' or 'old', CANT be None") + + list_mouse_press_event = [] + for tmp_mouse_btn_id, bool_pressed in enumerate(list_to_check): + if bool_pressed: + tmp_btn_name = get_mouse_btn_name(tmp_mouse_btn_id) + if (btn_name is None) or (btn_name.lower() == tmp_btn_name.lower()): + list_mouse_press_event.append( + {"btn_name": tmp_btn_name.lower(), "btn_id": tmp_mouse_btn_id}) + return list_mouse_press_event + + +def get_key_press(key_name=None): + return _get_key_press(key_name, "new") + + +def get_key_press_old(key_name=None): + return _get_key_press(key_name, "old") + + +def get_mouse_press(btn_name=None): + return _get_mouse_press(btn_name, "new") + + +def get_mouse_press_old(btn_name=None): + return _get_mouse_press(btn_name, "old") diff --git a/event.pyc b/event.pyc new file mode 100644 index 0000000..6342dfd Binary files /dev/null and b/event.pyc differ diff --git a/experiments.py b/experiments.py new file mode 100644 index 0000000..960a570 --- /dev/null +++ b/experiments.py @@ -0,0 +1,43 @@ +import inspect +def tryit(func): + print func + try: + print func.im_class + except: + print("Failed") + try: + print func.im_func + except: + print("Failed") + try: + print func.im_self + except: + print("Failed") + try: + func.asd=8 + except: + print("Failed again") + return func + +class meta_fun_Class(type): + + def __new__(cls, name, base, clsdict): + temp_class = type.__new__(cls, name, base, clsdict) + temp_class.__class__=cls + l=inspect.getmembers(temp_class,inspect.ismethod) + for l1 in l: + l1[1].asd=4 + return temp_class + +class A(object): + __metaclass__=meta_fun_Class + def b(cls): + pass + @classmethod + def c(cls): + pass + @staticmethod + def d(cls): + pass + + diff --git a/how i want/pong.py b/how i want/pong.py index 3885442..8b78320 100644 --- a/how i want/pong.py +++ b/how i want/pong.py @@ -1,25 +1,29 @@ import pyFun + + class pong(pyFun.fun_Game): + class bat(pyFun.fun_Class): - def __init__(self,up_key,down_key,*args,**kwargs): - super(bat,self).__init__(*args,**kwargs) - self.up_key=up_key - self.down_key=down_key - self.img=pyFun.shapes.rect(w=24,h=96) + + def __init__(self, up_key, down_key, *args, **kwargs): + super(bat, self).__init__(*args, **kwargs) + self.up_key = up_key + self.down_key = down_key + self.img = pyFun.shapes.rect(w=24, h=96) def action_create(self): - self.move_step=3 + self.move_step = 3 - def action_key_press(self,key): - if key==self.up_key: - self.y-=self.move_step - if key==self.down_key: - self.y+=self.move_step + def action_key_press(self, key): + if key == self.up_key: + self.y -= self.move_step + if key == self.down_key: + self.y += self.move_step def action_touch_room_boundary(self): - self.y=self.y_prev + self.y = self.y_prev -game=pong(width=760,height=640,fps=60) -game.bat(up_key=k_up,down_key=k_down,x=80,y=game.h/2) -game.bat(up_key=k_w,down_key=k_s,x=game.w-80,y=game.h/2) -game.run() \ No newline at end of file +game = pong(width=760, height=640, fps=60) +game.bat(up_key=k_up, down_key=k_down, x=80, y=game.h / 2) +game.bat(up_key=k_w, down_key=k_s, x=game.w - 80, y=game.h / 2) +game.run() diff --git a/load_map.py b/load_map.py new file mode 100644 index 0000000..2abd72c --- /dev/null +++ b/load_map.py @@ -0,0 +1,103 @@ +import importlib +import sys +import os +import inspect +import room_sprite_panel +import json + +dict_shape_panel = room_sprite_panel.CanvasPanel.dict_sprite_panel + + +def create_fake_cls(cls_name): + class fake_cls(object): + pass + + # cls_name is unicode, so str() it + fake_cls.__name__ = str(cls_name) + return fake_cls + + +class obj_ext(object): + pass + + +def append_filepath(filepath): + if filepath: + sys.path.append(filepath) + + +def import_module(filename, filepath=None): + append_filepath(filepath) + module_ = importlib.import_module(filename) + return module_ + + +def import_file(filename, filepath=None): + if not (filepath is None): + full_filename = os.path.join(filepath, filename) + print full_filename + else: + full_filename = filename + file_ = open(full_filename, "r") + return file_ + + +def filter_class_with_panel(module_): + list_class = inspect.getmembers(module_, inspect.isclass) + list_filtered_cls = [] + for (cls_name, cls_obj) in list_class: + if hasattr(cls_obj, "sprite_defaults"): + tmp_sprite = cls_obj.sprite_defaults["type"] + if tmp_sprite.map_class in dict_shape_panel.keys(): + cls_obj.sprite_defaults["panel"] = dict_shape_panel[ + tmp_sprite.map_class] + list_filtered_cls.append(cls_obj) + + return list_filtered_cls + + +def load_module(filename_or_module, filepath=None): + if isinstance(filename_or_module, str) or isinstance(filename_or_module, unicode): + module_ = import_module(filename_or_module, filepath) + elif inspect.ismodule(filename_or_module): + if filepath is None: + module_ = filename_or_module + else: + raise TypeError("filepath must be None if first param is module") + + return filter_class_with_panel(module_) + + +def load_json(filename_or_file, filepath=None): + if isinstance(filename_or_file, str) or isinstance(filename_or_file, unicode): + file_ = import_file(filename_or_file, filepath) + elif isinstance(filename_or_file, file): + file_ = filename_or_file + + data_json = json.load(file_) + if isinstance(file_, file): + file_.close() + + list_class_data = data_json["class_data"] + list_inst_data = data_json["instance_data"] + list_loaded_cls = [] + + for each_cls in list_class_data: + tmp_cls = create_fake_cls(each_cls["game_class"]) + + tmp_type = obj_ext() + tmp_type.map_class = each_cls["game_shape_class"] + tmp_cls.sprite_defaults = { + # "panel": each_cls["pygame_class"], + "params": each_cls["params"], + "type": tmp_type + } + + # add panel to sprite_defaults + if each_cls["game_shape_class"] in dict_shape_panel.keys(): + tmp_cls.sprite_defaults["panel"] = dict_shape_panel[ + each_cls["game_shape_class"]] + + list_loaded_cls.append(tmp_cls) + + return (list_loaded_cls, list_inst_data) diff --git a/load_map.pyc b/load_map.pyc new file mode 100644 index 0000000..e2b9c97 Binary files /dev/null and b/load_map.pyc differ diff --git a/map.json b/map.json new file mode 100644 index 0000000..fd5d5d8 --- /dev/null +++ b/map.json @@ -0,0 +1,148 @@ +{ + "instance_data": [ + { + "game_class": "wall", + "config": { + "color": null, + "center": [ + 121, + 183 + ], + "size": [ + 60, + 40 + ] + }, + "game_shape_class": "Rect" + }, + { + "game_class": "ball", + "config": { + "color": null, + "center": [ + 175, + 76 + ], + "size": [ + 80, + 40 + ] + }, + "game_shape_class": "Rect" + }, + { + "game_class": "wall", + "config": { + "color": null, + "center": [ + 62, + 182 + ], + "size": [ + 60, + 40 + ] + }, + "game_shape_class": "Rect" + }, + { + "game_class": "wall", + "config": { + "color": null, + "center": [ + 54, + 137 + ], + "size": [ + 60, + 40 + ] + }, + "game_shape_class": "Rect" + }, + { + "game_class": "wall", + "config": { + "color": null, + "center": [ + 120, + 144 + ], + "size": [ + 60, + 40 + ] + }, + "game_shape_class": "Rect" + }, + { + "game_class": "ball", + "config": { + "color": null, + "center": [ + 104, + 88 + ], + "size": [ + 80, + 40 + ] + }, + "game_shape_class": "Rect" + }, + { + "game_class": "ball", + "config": { + "color": null, + "center": [ + 72, + 224 + ], + "size": [ + 80, + 40 + ] + }, + "game_shape_class": "Rect" + }, + { + "game_class": "ball", + "config": { + "color": null, + "center": [ + 177, + 169 + ], + "size": [ + 80, + 40 + ] + }, + "game_shape_class": "Rect" + } + ], + "class_data": [ + { + "game_class": "wall", + "params": { + "color": null, + "size": [ + 60, + 40 + ] + }, + "game_shape_class": "Rect" + }, + { + "game_class": "ball", + "params": { + "color": null, + "size": [ + 80, + 40 + ] + }, + "game_shape_class": "Rect" + } + ] +} \ No newline at end of file diff --git a/map_2.json b/map_2.json new file mode 100644 index 0000000..c778bf7 --- /dev/null +++ b/map_2.json @@ -0,0 +1,136 @@ +{ + "instance_data": [{ + "game_class": "wall", + "config": { + "color": null, + "center": [ + 121, + 183 + ], + "size": [ + 60, + 40 + ] + }, + "game_shape_class": "Rect" + }, { + "game_class": "ball", + "config": { + "color": null, + "center": [ + 175, + 76 + ], + "size": [ + 80, + 40 + ] + }, + "game_shape_class": "Rect" + }, { + "game_class": "wall", + "config": { + "color": null, + "center": [ + 62, + 182 + ], + "size": [ + 60, + 40 + ] + }, + "game_shape_class": "Rect" + }, { + "game_class": "wall", + "config": { + "color": null, + "center": [ + 61, + 144 + ], + "size": [ + 60, + 40 + ] + }, + "game_shape_class": "Rect" + }, { + "game_class": "wall", + "config": { + "color": null, + "center": [ + 120, + 144 + ], + "size": [ + 60, + 40 + ] + }, + "game_shape_class": "Rect" + }, { + "game_class": "ball", + "config": { + "color": null, + "center": [ + 104, + 88 + ], + "size": [ + 80, + 40 + ] + }, + "game_shape_class": "Rect" + }, { + "game_class": "ball", + "config": { + "color": null, + "center": [ + 72, + 224 + ], + "size": [ + 80, + 40 + ] + }, + "game_shape_class": "Rect" + }, { + "game_class": "ball", + "config": { + "color": null, + "center": [ + 177, + 169 + ], + "size": [ + 80, + 40 + ] + }, + "game_shape_class": "Rect" + }], + "class_data": [{ + "game_class": "wall", + "params": { + "color": null, + "size": [ + 60, + 40 + ] + }, + "game_shape_class": "Rect" + }, { + "game_class": "ball", + "params": { + "color": null, + "size": [ + 80, + 40 + ] + }, + "game_shape_class": "Rect" + }] +} diff --git a/math_utils.py b/math_utils.py new file mode 100644 index 0000000..065b01b --- /dev/null +++ b/math_utils.py @@ -0,0 +1,122 @@ + +# Maybe make a non-square rooted function for comparison (faster) +from uniquelist import uniquelist +import math + + +def atan2_inv(y_diff, x_diff): + '''Returns atan2 in inverted y-axis in degrees''' + return math.degrees(math.atan2(-y_diff, x_diff)) +# following constants are required for the next function + + +def distance(point_1, point_2): + ''' distance between point_1 and point_2, where both are 2 member iterable''' + if (not len(point_1) == 2) or (not len(point_2) == 2): + raise TypeError("point_1 and point_2 both must be 2 member iterable") + diff_x = point_2[0] - point_1[0] + diff_y = point_2[1] - point_1[1] + return math.hypot(diff_x, diff_y) + + +def vel_add(vel_1, vel_2): + ''' vel_1 and vel_2 both must be in (magnitude,direction_in_degree) format. + Returns in same format.''' + if (not len(vel_1) == 2) or (not len(vel_2) == 2): + raise TypeError("vel_1 and vel_2 both must be 2 member iterable") + + vel_1_x = vel_1[0] * math.cos(math.radians(vel_1[1])) + vel_1_y = -vel_1[0] * math.sin(math.radians(vel_1[1])) + + vel_2_x = vel_2[0] * math.cos(math.radians(vel_2[1])) + vel_2_y = -vel_2[0] * math.sin(math.radians(vel_2[1])) + + vel_tot_x = vel_1_x + vel_2_x + vel_tot_y = vel_1_y + vel_2_y + + vel_tot = ( + math.hypot(vel_tot_x, vel_tot_y), atan2_inv(vel_tot_y, vel_tot_x)) + + return vel_tot + + +def direction(point_1, point_2): + if (not len(point_1) == 2) or (not len(point_2) == 2): + raise TypeError("point_1 and point_2 both must be 2 member iterable") + diff_x = point_2[0] - point_1[0] + diff_y = point_2[1] - point_1[1] + return atan2_inv(diff_y, diff_x) + +UNION = 1 +INTERSECTION = 2 +DIFFERENCE = 3 +ADD = 4 +# for each member (usually objects) in the list, it is replaced by one of +# its variables +MAKE_IT_VARIABLE_LIST = 5 + + +def operation_on_lists(operation, list1, list2=None, variable_name=None): + ''' operation_on_lists(operation, list1, list2) -> list + Does operation involving list1 and list2. + variable_name is required only for MAKE_IT_VARIABLE_LIST operation. It is a string representing the variable name + Valid operations are UNION, INTERSECTION, DIFFERENCE, ADD, MAKE_IT_VARIABLE_list ''' + def intersection(): + if list2 is None: + raise TypeError("second argument i.e. list2 should be a list") + list_return = [] + for temp1 in list1: + for temp2 in list2: + if temp1 == temp2: + list_return.append(temp1) + return list_return + + def union(): + if list2 is None: + raise TypeError("second argument i.e. list2 should be a list") + list_return = uniquelist() + list_return.extend(list1) + list_return.extend(list2) + return list_return + + def add(): + if list2 is None: + raise TypeError("second argument i.e. list2 should be a list") + list_return = [] + list_return.append(list1) + list_return.append(list2) + return list_return + + def difference(): + if list2 is None: + raise TypeError("second argument i.e. list2 should be a list") + list_return = [] + list_intersection = intersection() + for temp1 in list1: + if temp1 not in list_intersection: + list_return.append(temp1) + return list_return + + # for each member (usually objects) in the list, it is replaced by one of + # its variables + def make_it_variable_list(): + if variable_name is None: + raise TypeError("variable_name should be a string") + if not list2 is None: + raise TypeError("list2 should be None") + return_list = [getattr(temp, variable_name) for temp in list1] + + if operation == UNION: + return union() + elif operation == INTERSECTION: + return intersection() + elif operation == DIFFERENCE: + return difference() + elif operation == ADD: + return add() + elif operation == MAKE_IT_VARIABLE_LIST: + return make_it_variable_list() + else: + raise ValueError( + "Invalid operation name. Valid operations are UNION, INTERSECTION, DIFFERENCE.") +# end of operation_on_lists() diff --git a/math_utils.pyc b/math_utils.pyc new file mode 100644 index 0000000..2b0030e Binary files /dev/null and b/math_utils.pyc differ diff --git a/pyFun.sublime-project b/pyFun.sublime-project new file mode 100644 index 0000000..9cbd597 --- /dev/null +++ b/pyFun.sublime-project @@ -0,0 +1,9 @@ +{ + "folders": + [ + { + "follow_symlinks": true, + "path": "." + } + ] +} diff --git a/pyFun.sublime-workspace b/pyFun.sublime-workspace new file mode 100644 index 0000000..00a2f58 --- /dev/null +++ b/pyFun.sublime-workspace @@ -0,0 +1,1662 @@ +{ + "auto_complete": + { + "selected_items": + [ + [ + "get", + "get_switch_func" + ], + [ + "sw", + "switch_func" + ], + [ + "from", + "from_ statement" + ], + [ + "ca", + "call_func param" + ], + [ + "_c", + "_current_dict statement" + ], + [ + "st", + "str class" + ], + [ + "fr", + "from_ param" + ], + [ + "sel", + "self" + ], + [ + "sta", + "state_name forflow" + ], + [ + "li", + "list class" + ], + [ + "for", + "format" + ], + [ + "ha", + "has_state" + ], + [ + "fro", + "from_ statement" + ], + [ + "uni", + "uniquelist import" + ], + [ + "un", + "uniquelist import" + ], + [ + "state", + "state_name" + ], + [ + "_d", + "_current_dict statement" + ], + [ + "att", + "attr_name" + ], + [ + "_", + "_current_dict" + ], + [ + "tm", + "tmp_type statement" + ], + [ + "ga", + "game_shape_class" + ], + [ + "obj", + "obj_ext class" + ], + [ + "sp", + "sprite_panel" + ], + [ + "pa", + "params param" + ], + [ + "tmp", + "tmp_dict" + ], + [ + "data", + "data_loaded_inst statement" + ], + [ + "da", + "list_inst_data" + ], + [ + "list_cl", + "list_class_data statement" + ], + [ + "lis", + "list_class_data statement" + ], + [ + "para", + "params" + ], + [ + "ex", + "export_game_cls function" + ], + [ + "rtr", + "return keyword" + ], + [ + "val", + "val_return statement" + ], + [ + "dic", + "dict_loaded_cls" + ], + [ + "ret", + "return keyword" + ], + [ + "game", + "game_cls param" + ], + [ + "gam", + "game_class" + ], + [ + "cl", + "class_data statement" + ], + [ + "pan", + "panel_cls" + ], + [ + "pro", + "prompt-read" + ], + [ + "_star", + "_start_left statement" + ], + [ + "_st", + "_start_w statement" + ], + [ + "co", + "clientsock.connect_ex" + ], + [ + "So", + "socket class" + ], + [ + "load_", + "load_saved_json function" + ], + [ + "crea", + "create_box_sizer" + ], + [ + "cre", + "create_panel_ctrl" + ], + [ + "def", + "default_center param" + ], + [ + "cr", + "create_shape_panel function" + ], + [ + "gen", + "generate_loaded_shapes" + ], + [ + "up", + "update" + ], + [ + "pos", + "pos_wrt_parent statement" + ], + [ + "Scree", + "ScreenToClient function" + ], + [ + "Client", + "ClientToScreen function" + ], + [ + "act", + "action_mouse_left_up" + ], + [ + "EVT_LEFT_", + "EVT_LEFT_UP statement" + ], + [ + "JOIN_", + "JOIN_ROUND statement" + ], + [ + "Join_", + "JOIN_MITER statement" + ], + [ + "Set", + "SetJoin function" + ], + [ + "PENSTYL", + "PENSTYLE_LONG_DASH statement" + ], + [ + "NullP", + "NullPen statement" + ], + [ + "Null", + "NullBrush statement" + ], + [ + "foc", + "focus_text statement" + ], + [ + "fil", + "filename param" + ], + [ + "fa", + "fake_class class" + ], + [ + "cla", + "class New Class" + ], + [ + "re", + "read function" + ], + [ + "app", + "append_filepath function" + ], + [ + "lo", + "load_module function" + ], + [ + "imp", + "import_module function" + ], + [ + "str", + "str_ forflow" + ], + [ + "di", + "dict_loaded_cls statement" + ], + [ + "po", + "pop function" + ], + [ + "pop", + "pop function" + ], + [ + "cls", + "cls_obj forflow" + ], + [ + "roo", + "room_sprite_panel import" + ], + [ + "py", + "pygame_class" + ], + [ + "loa", + "load_map import" + ], + [ + "ins", + "inspect import" + ], + [ + "im", + "import_module function" + ], + [ + "wri", + "write function" + ], + [ + "fi", + "file class" + ], + [ + "js", + "jsoned_data statement" + ], + [ + "panel_", + "panel_ctrl" + ], + [ + "el", + "else keyword" + ], + [ + "i", + "isinstance function" + ], + [ + "jso", + "jsoned_data statement" + ], + [ + "Cl", + "class" + ], + [ + "bin", + "bind_events_default" + ], + [ + "bi", + "bind_events_default" + ], + [ + "Dr", + "DrawCircle function" + ], + [ + "ac", + "action_move function" + ], + [ + "EVT_C", + "EVT_CHAR statement" + ], + [ + "wxk_numpad_s", + "WXK_NUMPAD_SUBTRACT statement" + ], + [ + "action_", + "action_inflate function" + ], + [ + "Get", + "GetUniChar" + ], + [ + "G", + "GetKeyCode" + ], + [ + "EVT_I", + "EVT_IDLE statement" + ], + [ + "updat", + "update_internal" + ], + [ + "pr", + "print function" + ], + [ + "new", + "new_h statement" + ], + [ + "SetSi", + "SetSizeWH function" + ], + [ + "GEtsi", + "GetSizeTuple function" + ], + [ + "GETPo", + "GetPositionTuple function" + ], + [ + "sh", + "Show function" + ], + [ + "Add", + "AddChild function" + ], + [ + "Rec", + "RectPanel class" + ], + [ + "EVT_FOCUS", + "EVT_SET_FOCUS statement" + ], + [ + "u", + "updater_list class" + ], + [ + "su", + "super class" + ], + [ + "upda", + "updater_func" + ], + [ + "me", + "method_ statement" + ], + [ + "Se", + "SetStringItem" + ], + [ + "list", + "list_shape param" + ], + [ + "it", + "items" + ], + [ + "lsi", + "list_shape param" + ], + [ + "setsi", + "SetSizer function" + ], + [ + "dir", + "direction function" + ], + [ + "ata", + "atan2_inv function" + ], + [ + "poi", + "point_2" + ], + [ + "vel", + "vel_dir param" + ], + [ + "ve", + "vel_add function" + ], + [ + "mat", + "math_utils import" + ], + [ + "diff", + "diff_y statement" + ], + [ + "No", + "None statement" + ], + [ + "cle", + "clear_event_local function" + ], + [ + "list_e", + "list_event_local" + ], + [ + "dis", + "dispatch_events_self" + ] + ] + }, + "buffers": + [ + { + "file": "base.py", + "settings": + { + "buffer_size": 16278, + "line_ending": "Windows" + } + }, + { + "file": "event.py", + "settings": + { + "buffer_size": 16437, + "line_ending": "Unix", + "read_only": true + } + }, + { + "file": "/E/Babu bckup/Sublime Text Build 3059/Data/Packages/HTML-CSS-JS Prettify/HTMLPrettify.sublime-settings", + "settings": + { + "buffer_size": 293, + "line_ending": "Unix" + } + }, + { + "file": "todo.txt", + "settings": + { + "buffer_size": 4096, + "line_ending": "Windows" + } + }, + { + "file": "load_map.py", + "settings": + { + "buffer_size": 2968, + "line_ending": "Unix" + } + }, + { + "file": "map.json", + "settings": + { + "buffer_size": 3435, + "line_ending": "Windows" + } + }, + { + "file": "room_editor.py", + "settings": + { + "buffer_size": 7369, + "line_ending": "Windows" + } + }, + { + "file": "room_editor_old.py", + "settings": + { + "buffer_size": 6439, + "line_ending": "Windows" + } + }, + { + "file": "state_machine.py", + "settings": + { + "buffer_size": 2408, + "line_ending": "Unix" + } + }, + { + "file": "room_sprite_panel.py", + "settings": + { + "buffer_size": 9301, + "line_ending": "Unix" + } + }, + { + "file": "utils.py", + "settings": + { + "buffer_size": 9666, + "line_ending": "Unix" + } + }, + { + "file": "math_utils.py", + "settings": + { + "buffer_size": 4209, + "line_ending": "Unix" + } + }, + { + "file": "test_explicit_blit.py", + "settings": + { + "buffer_size": 1809, + "line_ending": "Unix" + } + }, + { + "file": "sprite_types.py", + "settings": + { + "buffer_size": 494, + "line_ending": "Unix" + } + }, + { + "file": "Circle.py", + "settings": + { + "buffer_size": 18534, + "line_ending": "Unix" + } + }, + { + "file": "Rect.py", + "settings": + { + "buffer_size": 19902, + "line_ending": "Unix" + } + }, + { + "file": "Img.py", + "settings": + { + "buffer_size": 15020, + "line_ending": "Unix" + } + } + ], + "build_system": "", + "command_palette": + { + "height": 400.0, + "selected_items": + [ + [ + "clea", + "Package Control: Install Package" + ], + [ + "cl", + "SublimeREPL: Clisp" + ], + [ + "res", + "SublimeREPL: Restart REPL" + ], + [ + "cli", + "SublimeREPL: Clisp" + ], + [ + "lisp", + "SublimeREPL: Clisp" + ], + [ + "lis", + "Set Syntax: Lisp" + ], + [ + "li", + "Package Control: List Packages" + ], + [ + "dis", + "Package Control: Disable Package" + ], + [ + "text", + "Set Syntax: Plain Text" + ], + [ + "in", + "Package Control: Install Package" + ], + [ + "i", + "Package Control: Install Package" + ], + [ + "ip", + "SublimeREPL: Python - IPython" + ], + [ + "run", + "SublimeREPL: Python - RUN current file" + ], + [ + "sw ", + "Convert Case: Swap Case" + ], + [ + "swap", + "Convert Case: Swap Case" + ], + [ + "caps", + "Convert Case: Swap Case" + ], + [ + "Package Control: ", + "Package Control: List Packages" + ], + [ + "re", + "Package Control: Remove Package" + ], + [ + "pdb", + "SublimeREPL: Python - PDB current file" + ], + [ + "ru ", + "SublimeREPL: Python - RUN current file" + ], + [ + "pyto", + "SublimeREPL: Python - PDB current file" + ], + [ + "py", + "SublimeREPL: Python" + ], + [ + "rem", + "Package Control: Remove Package" + ], + [ + "list", + "Package Control: List Packages" + ], + [ + "ana", + "Anaconda: Autoformat PEP8 Errors" + ], + [ + "IP", + "SublimeREPL: Python - IPython" + ], + [ + "restart", + "SublimeREPL: Restart REPL" + ], + [ + "rest", + "SublimeREPL: Restart REPL" + ], + [ + "ipy", + "SublimeREPL: Python - IPython" + ], + [ + "an", + "Anaconda: Autoformat PEP8 Errors" + ], + [ + "packa", + "Package Control: List Packages" + ], + [ + "mac", + "Anaconda: McCabe complexity check" + ], + [ + "anaconda", + "Anaconda: McCabe complexity check" + ], + [ + "ins", + "Package Control: Install Package" + ], + [ + "ht", + "Set Syntax: HTML" + ], + [ + "css", + "Set Syntax: CSS" + ], + [ + "Package Control: ins", + "Package Control: Install Package" + ] + ], + "width": 415.0 + }, + "console": + { + "height": 132.0, + "history": + [ + "import urllib.request,os,hashlib; h = '7183a2d3e96f11eeadd761d777e62404' + 'e330c659d4bb41d3bdf022e94cab3cd0'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)" + ] + }, + "distraction_free": + { + "menu_visible": true, + "show_minimap": false, + "show_open_files": false, + "show_tabs": false, + "side_bar_visible": false, + "status_bar_visible": false + }, + "file_history": + [ + "/E/Babu bckup/github_files/pyFun/state_machine.py", + "/E/Babu bckup/Sublime Text Build 3059/Data/Packages/anaconda/Anaconda.sublime-settings", + "/E/Babu bckup/Sublime Text Build 3059/Data/Packages/User/Anaconda.sublime-settings", + "/E/Program Files/wxPython3.0 Docs and Demos/wxPython/samples/wxPIA_book/Chapter-06/example1_mod.py", + "/D/Babu bckup/github_files/pyFun/map_2.json", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/SublimeREPL/SublimeREPL.sublime-settings", + "/E/Program Files/newlisp/newlisp-edit.lsp", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/HTML-CSS-JS Prettify/HTMLPrettify.sublime-settings", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/HTML-CSS-JS Prettify/.jsbeautifyrc", + "/C/Users/Machine/Desktop/a.html", + "/C/Users/Machine/Desktop/compython.py", + "/D/Babu bckup/github_files/pyFun/room_editor_old.py", + "/D/Babu bckup/github_files/map.json", + "/E/Program Files/wxPython3.0 Docs and Demos/wxPython/samples/wxPIA_book/Chapter-06/example1.py", + "/D/Babu bckup/github_files/pyFun/Rotated_Rect.py", + "/D/Babu bckup/github_files/pyFun/base_old.py", + "/D/Babu bckup/github_files/pyFun/math_utils.py", + "/D/Babu bckup/github_files/pyFun/Upcoming implementations.markdown", + "/D/Babu bckup/github_files/pyFun/awesome_try.py", + "/D/Babu bckup/github_files/pyFun/README.html", + "/D/Babu bckup/github_files/pyFun/event.py", + "/D/Babu bckup/github_files/pyFun/base.pyc", + "/D/Babu bckup/github_files/pyFun/utils_old.py", + "/D/Babu bckup/github_files/pyFun/Event.py", + "/D/Babu bckup/github_files/pyFun/Rect.py", + "/D/Babu bckup/github_files/pyFun/sprite_types.py", + "/D/Babu bckup/github_files/pyFun/utils.py", + "/C/Users/Machine/Downloads/Compressed/Brython2.1.2-20140607-093129/Brython2.1.2-20140607-093129/brython.js", + "/D/Babu bckup/github_files/pyFun/uniquelist.py", + "/D/Babu bckup/github_files/pyFun/base.py", + "/D/Babu bckup/github_files/blog/trial_canvas.js", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/SublimeLinter/SublimeLinter.sublime-settings", + "/D/Babu bckup/github_files/pyFun/test_event_action.py", + "/D/Babu bckup/github_files/pyFun/test_event_action", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/User/Preferences.sublime-settings", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/TernJS/TernJS.sublime-settings", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/User/TernJS.sublime-settings", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/Default/Preferences.sublime-settings", + "/D/Babu bckup/github_files/pyFun/utils_exp.py", + "/D/Babu bckup/github_files/pyFun/base_old_0.py", + "/D/Babu bckup/github_files/pyFun/awesome_better.py", + "/D/Babu bckup/github_files/pyFun/0.py", + "/D/Babu bckup/github_files/pyFun/test_register_event", + "/D/Babu bckup/github_files/pyFun/test_color_fill.py", + "/D/Babu bckup/github_files/pyFun/basic.py", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/anaconda/Anaconda.sublime-settings", + "/E/Python27/Lib/site-packages/pygame/constants.pyd", + "/E/Python27/Lib/site-packages/pygame/locals.py", + "/D/Babu bckup/github_files/pyFun/ip.py", + "/D/Babu bckup/github_files/pyFun/test_color_fill_all_check.py", + "/D/Babu bckup/github_files/pyFun/test_basic.py", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/Default/Default (Windows).sublime-keymap", + "/C/Users/Machine/Downloads/nunjucks.js", + "/C/Users/Machine/Downloads/ractive.js", + "/C/Users/Machine/Downloads/nunjucks-slim.js", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/User/Side Bar.sublime-settings", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/SideBarEnhancements/Side Bar.sublime-settings", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/User/Default (Windows).sublime-keymap", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/SideBarEnhancements/Default (Windows).sublime-keymap", + "/D/Babu bckup/Sublime Text Build 3059/Data/Packages/Emmet/Emmet.sublime-settings", + "/E/Babu/github_files/pyFun/how i want/pong.py", + "/E/Babu/github_files/pyFun/uniquelist.py", + "/E/Babu/github_files/pyFun/base.py", + "/E/Babu/Sublime Text Build 3059/Data/Packages/User/Anaconda.sublime-settings", + "/E/Babu/Sublime Text Build 3059/Data/Packages/anaconda/Anaconda.sublime-settings", + "/C/Documents and Settings/Machine/Desktop/a.py", + "/C/Documents and Settings/Machine/Desktop/ball.py", + "/C/Documents and Settings/Machine/My Documents/Downloads/Compressed/pygame-1.9.1release/lib/camera.py", + "/C/Documents and Settings/Machine/My Documents/Downloads/Compressed/pygame-1.9.1release/lib/sndarray.py", + "/E/Babu/Sublime Text Build 3059/Data/Packages/User/Preferences.sublime-settings", + "/E/Babu/Sublime Text Build 3059/Data/Packages/Default/Preferences.sublime-settings" + ], + "find": + { + "height": 36.0 + }, + "find_in_files": + { + "height": 98.0, + "where_history": + [ + ] + }, + "find_state": + { + "case_sensitive": false, + "find_history": + [ + "str_", + "self.full_file_path", + "self", + "tmp_path", + "create_shape_panel_from_str", + "config", + "default_center", + "generate_loaded_shapes", + "each_inst", + "config", + "foo", + "create", + "for_all_panel_class", + "list_panel_inst", + "list_panel_instance", + "list_shape", + "dict_sprite_panel", + "list_shape", + "left", + "# \"panel\": self.__class__.__name__,", + "pygame_class", + "_start_", + "_start", + "w", + "create_internal", + "action_mouse_left_down", + "mouse_focus", + "evt", + "Pen", + "t_circle", + "CircleP", + "show_debug", + "bind_event", + "\"center\":", + "center", + "module", + "or_module", + "load_module", + "\"panel\"", + "export", + "create_shape_panel", + "get_p", + "new_rect", + "self.list_loaded_cls[0].sprite_defaults", + "list_l", + "list_loaded_", + "0", + "list_shape", + "filter_class_with_sprite", + "sprite", + "tmp_sprite_cls", + "sprite_de", + "spr", + "get_prop", + "register_sprite_panel", + "dict_sprite_panel", + "dialog", + "list_loaded_map", + "dialog", + "get_p", + "{", + "expo", + "'", + "{", + "expor", + "get_", + "{", + ".panel_canvas", + "# style=wx.TRANSPARENT_WINDOW", + "parent", + "height", + "_y", + "_x", + "x", + "w", + "self.y", + "self.x", + "self.w", + "height", + "width", + "keyText", + "Text", + "_SUBTRACT", + "ADD", + "wx.WXK_ADD", + "==", + "keyText", + "update_i", + "str(", + "h", + "_w", + "w", + "h", + "w", + "self.", + "old", + "w", + "1.05", + "Move(", + "extend", + "update_list_ctrl", + "updater_func", + "def", + " ", + "arg", + "x", + "shape_list", + "shape_list_ctrl", + "()\n", + "size=", + "size", + "/", + "panel_", + "3", + "2", + "dc =", + "self.buffer", + "except", + "Circle", + "register_event", + "register_e", + "register", + "return_vel[0]", + "self.", + "import", + "atan2", + "self.", + "x" + ], + "highlight": true, + "in_selection": false, + "preserve_case": false, + "regex": false, + "replace_history": + [ + ], + "reverse": false, + "show_context": true, + "use_buffer2": true, + "whole_word": false, + "wrap": true + }, + "groups": + [ + { + "selected": 2, + "sheets": + [ + { + "buffer": 0, + "file": "base.py", + "semi_transient": false, + "settings": + { + "buffer_size": 16278, + "regions": + { + }, + "selection": + [ + [ + 9049, + 9049 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 3180.0, + "zoom_level": 1.0 + }, + "stack_index": 14, + "type": "text" + }, + { + "buffer": 1, + "file": "event.py", + "semi_transient": false, + "settings": + { + "buffer_size": 16437, + "regions": + { + }, + "selection": + [ + [ + 5456, + 5456 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 2155.0, + "zoom_level": 1.0 + }, + "stack_index": 7, + "type": "text" + }, + { + "buffer": 2, + "file": "/E/Babu bckup/Sublime Text Build 3059/Data/Packages/HTML-CSS-JS Prettify/HTMLPrettify.sublime-settings", + "semi_transient": false, + "settings": + { + "buffer_size": 293, + "regions": + { + }, + "selection": + [ + [ + 0, + 0 + ] + ], + "settings": + { + "syntax": "Packages/JavaScript/JSON.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 0, + "type": "text" + }, + { + "buffer": 3, + "file": "todo.txt", + "semi_transient": false, + "settings": + { + "buffer_size": 4096, + "regions": + { + }, + "selection": + [ + [ + 394, + 394 + ] + ], + "settings": + { + "syntax": "Packages/Text/Plain text.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 6, + "type": "text" + }, + { + "buffer": 4, + "file": "load_map.py", + "semi_transient": false, + "settings": + { + "buffer_size": 2968, + "regions": + { + }, + "selection": + [ + [ + 2321, + 2321 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 825.0, + "zoom_level": 1.0 + }, + "stack_index": 5, + "type": "text" + }, + { + "buffer": 5, + "file": "map.json", + "semi_transient": false, + "settings": + { + "buffer_size": 3435, + "regions": + { + }, + "selection": + [ + [ + 0, + 0 + ] + ], + "settings": + { + "syntax": "Packages/JavaScript/JSON.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 4, + "type": "text" + }, + { + "buffer": 6, + "file": "room_editor.py", + "semi_transient": false, + "settings": + { + "buffer_size": 7369, + "regions": + { + }, + "selection": + [ + [ + 556, + 556 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 161.0, + "zoom_level": 1.0 + }, + "stack_index": 3, + "type": "text" + }, + { + "buffer": 7, + "file": "room_editor_old.py", + "semi_transient": false, + "settings": + { + "buffer_size": 6439, + "regions": + { + }, + "selection": + [ + [ + 624, + 624 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 177.0, + "zoom_level": 1.0 + }, + "stack_index": 1, + "type": "text" + }, + { + "buffer": 8, + "file": "state_machine.py", + "semi_transient": false, + "settings": + { + "buffer_size": 2408, + "regions": + { + }, + "selection": + [ + [ + 653, + 653 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 648.0, + "zoom_level": 1.0 + }, + "stack_index": 2, + "type": "text" + }, + { + "buffer": 9, + "file": "room_sprite_panel.py", + "semi_transient": false, + "settings": + { + "buffer_size": 9301, + "regions": + { + }, + "selection": + [ + [ + 5525, + 5525 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": -0.0, + "translation.y": 2031.0, + "zoom_level": 1.0 + }, + "stack_index": 10, + "type": "text" + }, + { + "buffer": 10, + "file": "utils.py", + "semi_transient": false, + "settings": + { + "buffer_size": 9666, + "regions": + { + }, + "selection": + [ + [ + 9666, + 9666 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 3194.0, + "zoom_level": 1.0 + }, + "stack_index": 13, + "type": "text" + }, + { + "buffer": 11, + "file": "math_utils.py", + "semi_transient": false, + "settings": + { + "buffer_size": 4209, + "regions": + { + }, + "selection": + [ + [ + 1646, + 1646 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 95.0, + "zoom_level": 1.0 + }, + "stack_index": 9, + "type": "text" + }, + { + "buffer": 12, + "file": "test_explicit_blit.py", + "semi_transient": false, + "settings": + { + "buffer_size": 1809, + "regions": + { + }, + "selection": + [ + [ + 1612, + 1612 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 447.0, + "zoom_level": 1.0 + }, + "stack_index": 8, + "type": "text" + }, + { + "buffer": 13, + "file": "sprite_types.py", + "semi_transient": false, + "settings": + { + "buffer_size": 494, + "regions": + { + }, + "selection": + [ + [ + 358, + 358 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 12, + "type": "text" + }, + { + "buffer": 14, + "file": "Circle.py", + "semi_transient": false, + "settings": + { + "buffer_size": 18534, + "regions": + { + }, + "selection": + [ + [ + 209, + 209 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 11, + "type": "text" + }, + { + "buffer": 15, + "file": "Rect.py", + "semi_transient": false, + "settings": + { + "buffer_size": 19902, + "regions": + { + }, + "selection": + [ + [ + 204, + 204 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 2257.0, + "zoom_level": 1.0 + }, + "stack_index": 15, + "type": "text" + }, + { + "buffer": 16, + "file": "Img.py", + "semi_transient": false, + "settings": + { + "buffer_size": 15020, + "regions": + { + }, + "selection": + [ + [ + 138, + 138 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 16, + "type": "text" + } + ] + } + ], + "incremental_find": + { + "height": 22.0 + }, + "input": + { + "height": 0.0 + }, + "layout": + { + "cells": + [ + [ + 0, + 0, + 1, + 1 + ] + ], + "cols": + [ + 0.0, + 1.0 + ], + "rows": + [ + 0.0, + 1.0 + ] + }, + "menu_visible": true, + "output.exec": + { + "height": 106.0 + }, + "output.find_results": + { + "height": 0.0 + }, + "project": "pyFun.sublime-project", + "replace": + { + "height": 40.0 + }, + "save_all_on_build": true, + "select_file": + { + "height": 0.0, + "selected_items": + [ + ], + "width": 0.0 + }, + "select_project": + { + "height": 0.0, + "selected_items": + [ + ], + "width": 0.0 + }, + "select_symbol": + { + "height": 0.0, + "selected_items": + [ + ], + "width": 0.0 + }, + "settings": + { + }, + "show_minimap": true, + "show_open_files": true, + "show_tabs": true, + "side_bar_visible": false, + "side_bar_width": 261.0, + "status_bar_visible": true, + "template_settings": + { + } +} diff --git a/room_editor.py b/room_editor.py new file mode 100644 index 0000000..55ec6be --- /dev/null +++ b/room_editor.py @@ -0,0 +1,219 @@ +#!/bin/env python +import wx +import json +import load_map +import os +import room_sprite_panel as sprite_panel +from copy import deepcopy + + +class MyFrame(wx.Frame): + + def __init__(self): + self.size = (500, 300) + wx.Frame.__init__(self, None, -1, "My Frame", size=self.size) + + self.create_panel_ctrl_canvas() + + self.create_box_sizer() + + self.bind_events_default() + + self.load_saved_json() + # todo: make load_module_py func + + def load_saved_json(self): + # Load map + wildcard_py = "Python source (*.py)|*.py|" \ + "Compiled Python (*.pyc)|*.pyc|" \ + "All files (*.*)|*.*" + wildcard_json = "Json file (*.json)|*.json" + dialog_file = wx.FileDialog(None, "Choose game.py file", os.getcwd(), + "", wildcard_json, wx.OPEN) + if dialog_file.ShowModal() == wx.ID_OK: + tmp_path = dialog_file.GetPath() + filename = os.path.basename(tmp_path) + # filename_without_ext = filename[:filename.rfind(".")] + filepath = os.path.dirname(tmp_path) + list_loaded_cls, data_loaded_inst = load_map.load_json( + filename, filepath) + self.dict_loaded_cls = {} + for each_cls in list_loaded_cls: + self.dict_loaded_cls[each_cls.__name__] = each_cls +## del list_loaded_cls + else: + raise TypeError("Enter a filename to proceed") + + self.sync_panel_ctrl(self.dict_loaded_cls.keys()) + self.generate_loaded_inst(data_loaded_inst) + dialog_file.Destroy() + + def create_panel_ctrl_canvas(self): + # create the list control + self.panel_ctrl = wx.Panel( + self, -1, size=(-2, -1)) + self.panel_ctrl.SetBackgroundColour("aquamarine") + self.panel_ctrl.SetMaxSize((250, -1)) + + self.panel_ctrl.list_ctrl = wx.ListCtrl( + self.panel_ctrl, -1, style=wx.LC_LIST, size=(200, 180)) + self.list_shape = [] + + self.panel_canvas = wx.Panel( + self, -1, size=(-3, -1)) + self.panel_canvas.SetBackgroundColour("light blue") + + def create_box_sizer(self): + bxszr = wx.BoxSizer(wx.HORIZONTAL) + bxszr.Add(self.panel_ctrl, 2, flag=wx.EXPAND) + bxszr.Add(self.panel_canvas, 3, flag=wx.EXPAND) + self.SetSizer(bxszr) + # self.Fit() + + def generate_loaded_inst(self, list_): + print(len(list_)) + for each_inst in list_: + self.create_shape_panel_from_str( + each_inst["game_class"], each_inst["config"]) + + def sync_panel_ctrl(self, list_str): + for index, str_ in enumerate(list_str): + if index > self.panel_ctrl.list_ctrl.GetItemCount() - 1: + method_ = self.panel_ctrl.list_ctrl.InsertStringItem + else: + method_ = self.panel_ctrl.list_ctrl.SetItemText + # try: + method_(index, str_) + # self.panel_ctrl.list_ctrl.Refresh() + # except: + # pass + + def create_shape_panel_from_str(self, str_, params=None): + tmp_game_cls = self.dict_loaded_cls[str_] + return self.create_shape_panel(tmp_game_cls, params) + + def create_shape_panel(self, game_cls, params=None): + tmp_dict = deepcopy(game_cls.sprite_defaults) + tmp_panel = tmp_dict["panel"] + if params is None: + params = {} + tmp_params = tmp_dict["params"] + tmp_params.update(params) + center = tmp_params.pop("center") + + tmp_shape_panel = tmp_panel( + self.panel_canvas, center=center, **tmp_params) + tmp_shape_panel.user_data = {"class": game_cls.__name__} + tmp_shape_panel.Show() + self.panel_canvas.Refresh() + self.list_shape.append(tmp_shape_panel) + return tmp_shape_panel + + def action_key_press(self, ev): + print self.__class__ + keyCode = ev.GetKeyCode() + try: + keyText = chr(keyCode).upper() + except: + keyText = None + print keyText + + if keyText == "E": + print self.export_data("map.json") + + def bind_events_default(self): + print "binding" + self.panel_ctrl.list_ctrl.Bind(wx.EVT_KEY_DOWN, self.action_key_press) + self.panel_canvas.Bind(wx.EVT_LEFT_DOWN, self.action_mouse_left_down) + + def action_mouse_left_down(self, ev): + focus_id = self.panel_ctrl.list_ctrl.FocusedItem + if (focus_id == -1): + focus_id = 0 + print "Setting focus" + self.panel_ctrl.list_ctrl.Focus(focus_id) + focus_id = self.panel_ctrl.list_ctrl.FocusedItem + + focus_text = self.panel_ctrl.list_ctrl.GetItem( + focus_id).GetText() + pos = ev.GetPosition() + tmp_panel = self.create_shape_panel_from_str( + focus_text, {"center": pos}) + tmp_panel.SetFocus() + + @staticmethod + def for_all_panel_class(func_): + return_list = [] + for tmp_panel_cls in sprite_panel.CanvasPanel.dict_sprite_panel.values(): + val_return = func_(tmp_panel_cls) + return_list.append(val_return) + + return return_list + + @staticmethod + def for_all_panel_inst(func_, flatten=False): + # todo: use for_all_panel_class + list_return = [] + for tmp_panel_cls in sprite_panel.CanvasPanel.dict_sprite_panel.values(): + tmp_list = [] + for tmp_panel_inst in tmp_panel_cls.list_panel_inst: + val_return = func_(tmp_panel_inst) + tmp_list.append(val_return) + if flatten: + list_return.extend(tmp_list) + else: + list_return.append(tmp_list) + + return list_return + + @staticmethod + def export_game_cls(game_cls): + dict_return = {} + dict_return["game_class"] = game_cls.__name__ + + tmp_sprite_defaults = deepcopy(game_cls.sprite_defaults) + dict_return["game_shape_class"] = tmp_sprite_defaults["type"].map_class + dict_return["params"] = tmp_sprite_defaults["params"] + + return dict_return + + def for_all_game_cls(self, func_): + list_return = [] + for tmp_game_cls in self.get_all_loaded_cls(): + val_return = func_(tmp_game_cls) + list_return.append(val_return) + return list_return + + def get_all_loaded_cls(self): + return self.dict_loaded_cls.values() + + def export_data(self, file_=None, pprint_=True): + + class_data = self.for_all_game_cls( + lambda game_cls: self.export_game_cls(game_cls)) + instance_data = self.for_all_panel_inst( + lambda panel_inst: panel_inst.get_props(), flatten=True) + + data = {"class_data": class_data, "instance_data": instance_data} + if pprint_: + jsoned_data = json.dumps(data, indent=4) + print jsoned_data + else: + jsoned_data = json.dumps(data) + + if file_ is None: + return jsoned_data + elif isinstance(file_, str): + file_ = open(file_, "w") + + if isinstance(file_, file): + file_.write(jsoned_data) + file_.close() + else: + raise TypeError("file_ parameter must be of file or str type.") + +if __name__ == '__main__': + app = wx.PySimpleApp() + frame = MyFrame() + frame.Show(True) + app.MainLoop() diff --git a/room_editor_old.py b/room_editor_old.py new file mode 100644 index 0000000..a5cc526 --- /dev/null +++ b/room_editor_old.py @@ -0,0 +1,189 @@ +#!/bin/env python +import wx +import json +import load_map +import os +import room_sprite_panel as sprite_panel +from copy import deepcopy + + +class MyFrame(wx.Frame): + + def __init__(self): + self.size = (500, 300) + wx.Frame.__init__(self, None, -1, "My Frame", size=self.size) + + self.panel_ctrl = wx.Panel( + self, -1, size=(-2, -1)) + self.panel_ctrl.SetBackgroundColour("aquamarine") + self.panel_ctrl.SetMaxSize((250, -1)) + + # load some images into an image list + il = wx.ImageList(16, 16, True) + il.Add(wx.EmptyBitmap(16, 16)) + + # create the list control + self.panel_ctrl.list_ctrl = wx.ListCtrl( + self.panel_ctrl, -1, style=wx.LC_LIST, size=(200, 180)) + # self.list_shape = [] + # Load map + wildcard_py = "Python source (*.py)|*.py|" \ + "Compiled Python (*.pyc)|*.pyc|" \ + "All files (*.*)|*.*" + dialog_file = wx.FileDialog(None, "Choose game.py file", os.getcwd(), + "", wildcard_py, wx.OPEN) + if dialog_file.ShowModal() == wx.ID_OK: + self.module_path = dialog_file.GetPath() + filename = os.path.basename(self.module_path) + filename_without_ext = filename[:filename.rfind(".")] + filepath = os.path.dirname(self.module_path) + list_loaded_cls = load_map.load_module( + filename_without_ext, filepath) + self.dict_loaded_cls = {} + for each_cls in list_loaded_cls: + self.dict_loaded_cls[each_cls.__name__] = each_cls + del list_loaded_cls + else: + raise TypeError("Enter a filename to proceed") + + self.sync_panel_ctrl(self.dict_loaded_cls.keys()) + + dialog_file.Destroy() + # assign the image list to it + # self.panel_ctrl.list_ctrl.AssignImageList(il, wx.IMAGE_LIST_SMALL) + self.panel_canvas = wx.Panel( + self, -1, size=(-3, -1)) + self.panel_canvas.SetBackgroundColour("light blue") + + bxszr = wx.BoxSizer(wx.HORIZONTAL) + bxszr.Add(self.panel_ctrl, 2, flag=wx.EXPAND) + bxszr.Add(self.panel_canvas, 3, flag=wx.EXPAND) + self.SetSizer(bxszr) + # self.Fit() + + self.bind_events_default() + + def sync_panel_ctrl(self, list_str): + for index, str_ in enumerate(list_str): + if index > self.panel_ctrl.list_ctrl.GetItemCount() - 1: + method_ = self.panel_ctrl.list_ctrl.InsertStringItem + else: + method_ = self.panel_ctrl.list_ctrl.SetItemText + # try: + method_(index, str_) + # self.panel_ctrl.list_ctrl.Refresh() + # except: + # pass + + def create_shape_panel(self, str_, default_center): + tmp_game_cls = self.dict_loaded_cls[str_] + tmp_dict = tmp_game_cls.sprite_defaults + + if "center" in tmp_dict: + center = tmp_dict["center"] + else: + center = default_center + + tmp_shape_panel = tmp_dict["panel"]( + self.panel_canvas, center=center, **tmp_dict["params"]) + tmp_shape_panel.user_data = {"class": tmp_game_cls.__name__} + tmp_shape_panel.Show() + self.panel_canvas.Refresh() + # self.list_shape.append(tmp_shape_panel) + + def action_key_press(self, ev): + print self.__class__ + keyCode = ev.GetKeyCode() + try: + keyText = chr(keyCode).upper() + except: + keyText = None + print keyText + + if keyText == "\r": + focus_id = self.panel_ctrl.list_ctrl.FocusedItem + focus_text = self.panel_ctrl.list_ctrl.GetItem(focus_id).GetText() + self.create_shape_panel(focus_text, (0, 0)) + if keyText == "E": + print self.export_data("map.json") + + def bind_events_default(self): + print "binding" + self.panel_ctrl.list_ctrl.Bind(wx.EVT_KEY_DOWN, self.action_key_press) + + @staticmethod + def for_all_panel_class(func_): + return_list = [] + for tmp_panel_cls in sprite_panel.CanvasPanel.dict_sprite_panel.values(): + val_return = func_(tmp_panel_cls) + return_list.append(val_return) + + return return_list + + @staticmethod + def for_all_panel_inst(func_, flatten=False): + # todo: use for_all_panel_class + list_return = [] + for tmp_panel_cls in sprite_panel.CanvasPanel.dict_sprite_panel.values(): + tmp_list = [] + for tmp_panel_inst in tmp_panel_cls.list_panel_inst: + val_return = func_(tmp_panel_inst) + tmp_list.append(val_return) + if flatten: + list_return.extend(tmp_list) + else: + list_return.append(tmp_list) + + return list_return + + @staticmethod + def export_game_cls(game_cls): + dict_return = {} + dict_return["game_class"] = game_cls.__name__ + + tmp_sprite_defaults = deepcopy(game_cls.sprite_defaults) + dict_return["game_shape_class"] = tmp_sprite_defaults["type"].map_class + dict_return["params"] = tmp_sprite_defaults["params"] + + return dict_return + + def for_all_game_cls(self, func_): + list_return = [] + for tmp_game_cls in self.get_all_loaded_cls(): + val_return = func_(tmp_game_cls) + list_return.append(val_return) + return list_return + + def get_all_loaded_cls(self): + return self.dict_loaded_cls.values() + + def export_data(self, file_=None, pprint_=True): + + class_data = self.for_all_game_cls( + lambda game_cls: self.export_game_cls(game_cls)) + instance_data = self.for_all_panel_inst( + lambda panel_inst: panel_inst.get_props(), flatten=True) + + data = {"class_data": class_data, "instance_data": instance_data} + if pprint_: + jsoned_data = json.dumps(data, indent=4) + print jsoned_data + else: + jsoned_data = json.dumps(data) + + if file_ is None: + return jsoned_data + elif isinstance(file_, str): + file_ = open(file_, "w") + + if isinstance(file_, file): + file_.write(jsoned_data) + file_.close() + else: + raise TypeError("file_ parameter must be of file or str type.") + +if __name__ == '__main__': + app = wx.PySimpleApp() + frame = MyFrame() + frame.Show(True) + app.MainLoop() diff --git a/room_sprite_panel.py b/room_sprite_panel.py new file mode 100644 index 0000000..8d78a38 --- /dev/null +++ b/room_sprite_panel.py @@ -0,0 +1,291 @@ +import wx + + +def register_sprite_panel(sprite_panel): + CanvasPanel.dict_sprite_panel[sprite_panel.game_shape_class] = sprite_panel + return sprite_panel + + +class CanvasPanel(wx.Panel): + + """Parent canvas panel""" + + dict_sprite_panel = {} + + def __init__(self, *arg, **kwargs): + super(CanvasPanel, self).__init__(*arg, **kwargs) + self.create_internal() + self.init_start_var() + self.perc = 5.0 / 100 + self.update = False + self.delta = None + + self.bind_events_default() + + def init_start_var(self): + if not hasattr(self, "_start_w"): + self._start_w = self.w + if not hasattr(self, "_start_h"): + self._start_h = self.h + if not hasattr(self, "_start_left"): + self._start_left = self.left + if not hasattr(self, "_start_top"): + self._start_top = self.top + + def create_internal(self): + self.w, self.h = self.GetSizeTuple() + self.left, self.top = self.GetPositionTuple() + size = self.GetClientSize() + self.buffer = wx.EmptyBitmap(max(1, size.width), max(1, size.height)) + + def bind_events_default(self): + self.Bind(wx.EVT_KEY_DOWN, self.action_move) + self.Bind(wx.EVT_CHAR_HOOK, self.action_inflate) + self.Bind(wx.EVT_IDLE, self.action_idle) + self.Bind(wx.EVT_LEFT_DOWN, self.action_mouse_left_down) + self.Bind(wx.EVT_LEFT_UP, self.action_mouse_left_up) + self.Bind(wx.EVT_SET_FOCUS, self.action_unfocused) + self.Bind(wx.EVT_KILL_FOCUS, self.action_unfocused) + self.Bind(wx.EVT_MOTION, self.action_dragged) + + def action_mouse_left_up(self, ev): + if self.HasCapture(): + self.ReleaseMouse() + self.delta = None + + def action_dragged(self, ev): + if ev.Dragging() and ev.LeftIsDown() and self.delta: + pos_wrt_self = ev.GetPosition() + pos_wrt_screen = self.ClientToScreen(pos_wrt_self) + pos_wrt_parent = self.GetParent().ScreenToClient(pos_wrt_screen) + pos = pos_wrt_parent + + newPos = (pos.x - self.delta.x, pos.y - self.delta.y) + self.Move(newPos) + self.left, self.top = self.GetPositionTuple() + self.update = True + + def action_move(self, ev): + keyText = chr(ev.GetUnicodeKey()).upper() + # print "keyText= " + str(keyText) + if keyText == "W": + self.top -= 5 + print "moving up" + if keyText == "A": + self.left -= 5 + print "moving left" + if keyText == "S": + self.top += 5 + print "moving down" + if keyText == "D": + self.left += 5 + print "moving right" + + self.update = True + ev.Skip() + return True + + def action_inflate(self, ev): + keyText = ev.GetKeyCode() # .upper() + + # print "char= " + str(keyText) + tmp_perc = None + if keyText == wx.WXK_ADD or keyText == wx.WXK_NUMPAD_ADD: + tmp_perc = self.perc + print "Inflating" + if keyText == wx.WXK_SUBTRACT or keyText == wx.WXK_NUMPAD_SUBTRACT: + tmp_perc = -self.perc + print "Deflating" + + if not (tmp_perc is None): + new_w = self.w * (1 + tmp_perc) + new_h = self.h * (1 + tmp_perc) + new_left = self.left - (new_w - self.w) / 2 + new_top = self.top - (new_h - self.h) / 2 + print "old_w, old_h" + str((self.w, self.h)) + print "new_w, new_h" + str((new_w, new_h)) + print "old_left, old_top" + str((self.left, self.top)) + print "new_left, new_top" + str((new_left, new_top)) + print "" + self.left, self.top = new_left, new_top + self.w, self.h = new_w, new_h + + self.update = True + ev.Skip() + return True + + @property + def center(self): + return (self.left + self.w / 2), (self.top + self.h / 2) + + def action_idle(self, ev): + if self.update: + self.SetPosition((self.left, self.top)) + self.SetSizeWH(self.w, self.h) + size = self.GetClientSize() + self.buffer = wx.EmptyBitmap( + max(1, size.width), max(1, size.height)) + self.GetParent().Refresh() + self.update = False + + def action_mouse_left_down(self, ev): + self.SetFocus() + self.CaptureMouse() + pos_wrt_self = ev.GetPosition() + pos_wrt_screen = self.ClientToScreen(pos_wrt_self) + pos_wrt_parent = self.GetParent().ScreenToClient(pos_wrt_screen) + pos = pos_wrt_parent + origin = self.GetPosition() + self.delta = wx.Point(pos.x - origin.x, pos.y - origin.y) + + def action_focused(self, ev): + self.Refresh() + + def action_unfocused(self, ev): + self.Refresh() + + +@register_sprite_panel +class RectPanel(CanvasPanel): + + """docstring for RectPanel""" + + game_shape_class = "Rect" + list_panel_inst = [] + + def __init__(self, *arg, **kwargs): + tmp_center = kwargs["center"] + tmp_size = kwargs["size"] + kwargs.pop("center") + kwargs.pop("color") + kwargs["pos"] = ( + (tmp_center[0] - tmp_size[0] / 2), (tmp_center[1] - tmp_size[1] / 2)) + super(RectPanel, self).__init__(*arg, **kwargs) + self.brush = wx.Brush("Green") + self.list_panel_inst.append(self) + + def get_props(self): + return { + "game_class": self.user_data["class"], + "game_shape_class": self.game_shape_class, + "config": { + "center": self.center, + "size": (self.w, self.h), + "color": None, + } + } + + def bind_events_default(self): + super(RectPanel, self).bind_events_default() + self.Bind(wx.EVT_PAINT, self.action_paint_rect) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.action_clear_none) + + @property + def pen_focused(self): + if not hasattr(self, "_pen_focused"): + self._pen_focused = wx.Pen( + "Blue", width=3, style=wx.PENSTYLE_LONG_DASH) + self._pen_focused.SetCap(wx.CAP_BUTT) + self._pen_focused.SetJoin(wx.JOIN_ROUND) + return self._pen_focused + + @pen_focused.setter + def pen_focused(self, val): + self._pen_focused = val + + def action_paint_rect(self, ev): + dc = wx.BufferedPaintDC(self, self.buffer) + dc.SetBackground(wx.Brush(wx.Colour(150, 150, 150, 100))) + dc.Clear() + if self.HasFocus(): + self.pen = self.pen_focused + else: + self.pen = wx.NullPen + + dc.SetBrush(self.brush) + dc.SetPen(self.pen) + + dc.DrawRectangle(0, 0, self.w, self.h) + # self.SetBackgroundColour("green") + + def action_clear_none(self, ev): + wx.BufferedDC(None, self.buffer) + + +@register_sprite_panel +class CirclePanel(CanvasPanel): + + """docstring for CirclePanel""" + + game_shape_class = "Circle" + list_panel_inst = [] + + def __init__(self, *arg, **kwargs): + tmp_center = kwargs["center"] + tmp_radius = kwargs["radius"] + kwargs.pop("color") + kwargs["pos"] = ( + (tmp_center[0] - tmp_radius), (tmp_center[1] - tmp_radius)) + kwargs.pop("center") + kwargs["size"] = (tmp_radius * 2, tmp_radius * 2) + kwargs.pop("radius") + super(CirclePanel, self).__init__(*arg, **kwargs) + self.brush = wx.Brush("Green") + self.list_panel_inst.append(self) + + def bind_events_default(self): + super(CirclePanel, self).bind_events_default() + self.Bind(wx.EVT_PAINT, self.action_paint_circle) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.action_clear_none) + + @property + def pen_focused(self): + if not hasattr(self, "_pen_focused"): + self._pen_focused = wx.Pen( + "Blue", width=3, style=wx.PENSTYLE_LONG_DASH) + self._pen_focused.SetCap(wx.CAP_BUTT) + self._pen_focused.SetJoin(wx.JOIN_ROUND) + return self._pen_focused + + @pen_focused.setter + def pen_focused(self, val): + self._pen_focused = val + + def action_paint_circle(self, ev): + dc = wx.BufferedPaintDC(self, self.buffer) + dc.SetBackground(wx.Brush(wx.Colour(150, 150, 150, 100))) + dc.Clear() + if self.HasFocus(): + self.pen = self.pen_focused + else: + self.pen = wx.NullPen + + dc.SetBrush(self.brush) + dc.SetPen(self.pen) + + dc.DrawCircle(self.w / 2, self.h / 2, self.w / 2) + + def action_clear_none(self, ev): + wx.BufferedDC(None, self.buffer) + + def get_props(self): + return { + "class": self.user_data["class"], + "game_shape_class": self.game_shape_class, + "config": { + "center": self.center, + "radius": self.w / 2, + "color": None, + } + } + + def get_start_props(self): + return { + "class": self.user_data["class"], + "game_shape_class": self.game_shape_class, + "config": { + "center": (self._start_left + self._start_w / 2, self._start_top + self._start_h / 2), + "radius": self._start_w / 2, + "color": None, + } + } diff --git a/room_sprite_panel.pyc b/room_sprite_panel.pyc new file mode 100644 index 0000000..cd902f0 Binary files /dev/null and b/room_sprite_panel.pyc differ diff --git a/sprite_types.py b/sprite_types.py new file mode 100644 index 0000000..0378e78 --- /dev/null +++ b/sprite_types.py @@ -0,0 +1,24 @@ +import Circle +import Rect +import Img + +# try..except saves from AttributeError during import +# => means import, -> means running line within that file +# why : sprite_types => Circle => sprite_types -> Circle.Circle +# this raises error, as Circle class not yet defined in Circle +# but when __main__ runs, after all successful imports, Circle.Circle is defined + +try: + Circle = Circle.Circle +except: + pass + +try: + Rect = Rect.Rect +except: + pass + +try: + Img = Img.Img +except: + pass diff --git a/sprite_types.pyc b/sprite_types.pyc new file mode 100644 index 0000000..a17135b Binary files /dev/null and b/sprite_types.pyc differ diff --git a/state_machine.py b/state_machine.py new file mode 100644 index 0000000..88f9893 --- /dev/null +++ b/state_machine.py @@ -0,0 +1,79 @@ +from copy import deepcopy +from uniquelist import uniquelist + + +class StateClass(object): + + """State engine implementation""" + + def __init__(self): + super(StateClass, self).__init__() + self._list_state = uniquelist() + self._current_state = None + self._dict_switch_func = [] + self._current_dict = {} + + def __getattribute__(self, name): + try: + return self._current_dict[name] + except KeyError: + raise AttributeError("'{self_class}' object has no attribute '{attr_name}'".format( + self_class=self.__class__, attr_name=name)) + + def __setattr__(self, name, val): + self._current_dict[name] = val + + def add_state(self, state_name): + self._list_state.append(str(state_name)) + + def has_state(self, state_name): + return str(state_name) in self._list_state + + def remove_state(self, state_name): + state_name = str(state_name) + self.check_state(state_name) + + self._list_state.remove(state_name) + + def check_state(self, state_name_or_list): + if isinstance(state_name_or_list, list): + for state_name in state_name_or_list: + self.check_state(state_name) + else: + state_name = state_name_or_list + + if self.has_state(state_name): + return state_name + else: + raise ValueError("state {state_name} not registered".format( + state_name=state_name)) + + def add_switch_func(self, from_, to_, func_): + from_, to_ = str(from_), str(to_) + self.check_state([from_, to_]) + + if (from_, to_) not in self._dict_switch_func: + self._dict_switch_func[(from_, to_)] = [] + + self._dict_switch_func[(from_, to_)].append(func_) + + def get_switch_func(self, from_, to_): + from_, to_ = str(from_), str(to_) + self.check_state([from_, to_]) + + return self._dict_switch_func.get((from_, to_)) + + def switch_state(self, from_, to_, call_func=True): + from_, to_ = str(from_), str(to_) + self.check_state([from_, to_]) + + # do call_func + if call_func: + switch_func = self.get_switch_func(from_, to_) + if switch_func: + self._current_dict = switch_func(self._current_dict) + else: + self._current_dict = {} + + self._current_state = to_ + # remove below diff --git a/test_color_fill.py b/test_color_fill.py new file mode 100644 index 0000000..dd77680 --- /dev/null +++ b/test_color_fill.py @@ -0,0 +1,39 @@ +import base +import random +import sys +from base import pygame + +tmp_fill_color = (156, 27, 210) +w = h = 200 +is_test_ok = None +tmp_i = 0 + + +class test_Game(base.fun_Game): + + def __init__(self, *arg, **kwarg): + super(test_Game, self).__init__(*arg, **kwarg) + self.fps = 100000 # max_fps + + def test_update(self): + self.screen.fill(tmp_fill_color) + tmp_px_array = pygame.PixelArray(self.screen) + is_test_ok = True + global tmp_i + if tmp_i < 1000: + tmp_i += 1 + tmp_w = random.randrange(0, w) + tmp_h = random.randrange(0, h) + if tmp_px_array[tmp_w][tmp_h] != self.screen.map_rgb(tmp_fill_color): + is_test_ok = False + else: + if is_test_ok: + print "Test OK" + else: + print "Test Broken" + sys.exit() + +a = test_Game(w, h) +a.run() + +# Should display a purple window diff --git a/test_color_fill_all_check.py b/test_color_fill_all_check.py new file mode 100644 index 0000000..1bf2b1e --- /dev/null +++ b/test_color_fill_all_check.py @@ -0,0 +1,42 @@ +import base +import sys +import random +from base import pygame + +tmp_fill_color = (156, 27, 210) +w = random.randint(0, 1336) +h = random.randint(0, 768) +is_test_ok = None +tmp_i = 0 + + +class test_Game(base.fun_Game): + + def test_update(self): + tmp_px_array = pygame.PixelArray(self.screen) + is_test_ok = True + global tmp_i + self.screen.fill(tmp_fill_color) + if tmp_i: + for tmp_w in range(w): + for tmp_h in range(h): + tmp_i += 1 + if tmp_px_array[tmp_w][tmp_h] != self.screen.map_rgb(tmp_fill_color): + is_test_ok = False + break + if not is_test_ok: + break + + print "Test run for %s pixels" % (tmp_i - 1) + if is_test_ok: + print "Test OK" + else: + print "Test Broken" + sys.exit() + tmp_i += 1 + +a = test_Game(w, h) +a.run() + +# Test made so that on first frame, screen is filled with purple. +# Rest of the test loop occurs on second frame diff --git a/test_color_fill_random_check.py b/test_color_fill_random_check.py new file mode 100644 index 0000000..35a7c86 --- /dev/null +++ b/test_color_fill_random_check.py @@ -0,0 +1,39 @@ +import base +import random +import sys +from base import pygame + +tmp_fill_color = (156, 27, 210) +w = h = 200 +is_test_ok = None +tmp_i = 0 + + +class test_Game(base.fun_Game): + + def test_update(self): + tmp_px_array = pygame.PixelArray(self.screen) + is_test_ok = True + global tmp_i + self.screen.fill(tmp_fill_color) + if tmp_i: + while tmp_i < 1000 and tmp_i: + tmp_i += 1 + tmp_w = random.randrange(0, w) + tmp_h = random.randrange(0, h) + if tmp_px_array[tmp_w][tmp_h] != self.screen.map_rgb(tmp_fill_color): + is_test_ok = False + break + + if is_test_ok: + print "Test OK" + else: + print "Test Broken" + sys.exit() + tmp_i += 1 + +a = test_Game(w, h) +a.run() + +# Test made so that on first frame, screen is filled with purple. +# Rest of the test loop occurs on second frame diff --git a/test_event_action.py b/test_event_action.py new file mode 100644 index 0000000..c1eaeb7 --- /dev/null +++ b/test_event_action.py @@ -0,0 +1,12 @@ +import base + +# make fun_class with some action handlers, who just print for debug + +# move to test +fun_Game.list_event.append(pygame.event.Event(KW_EVENTS_IB["JOY_BUTTON_UP"])) +fun_Game.list_event.append(pygame.event.Event(KW_EVENTS_IB["KEY_UP"])) +fun_Game.list_event.append(pygame.event.Event(KW_EVENTS_IB["KEY_DOWN"])) +fun_Game.process_event_list(fun_Game.list_event) +g = fun_Game(200, 200) +c = fun_Class(0, 0) +g.update_all_class() diff --git a/test_explicit_blit.py b/test_explicit_blit.py new file mode 100644 index 0000000..f723061 --- /dev/null +++ b/test_explicit_blit.py @@ -0,0 +1,76 @@ +from base import * +import pygame.locals as pygame_const +from sprite_types import Rect, Img, Circle +import event + +g = fun_Game(800, 200, fps=30) + +event.register_event_name("trIal") + + +def gen_TRIAL(): + return event.Event("trial", dict_={"haha": "hihi"}) +# event.register_custom_event_generator(gen_TRIAL) + + +@register_class(fun_Game) +class ball(fun_Class): + + sprite_defaults = { + "type": Rect, + "params": { + "size": (80, 40), + "color": None + # "file_name_or_obj":r"C:\Users\Machine\Pictures\Flight by Twitter_2.jpg" + } + } + + def __init__(self, *args, **kwargs): + super(ball, self).__init__(*args, **kwargs) + + def action_begin_step(self, ev): + self.next_x, self.next_y = self.center + + def action_key_press_right(self, ev): + if ev.dict["key_name"] == "right": + self.next_x += 5 + + if ev.dict["key_name"] == "left": + self.next_x -= 5 + + if ev.dict["key_name"] == "up": + self.next_y -= 5 + + if ev.dict["key_name"] == "down": + self.next_y += 5 + + def action_end_step(self, ev): +# if not self.collide(b, moved_center=(self.next_x, self.next_y)): +# if hasattr(self, "next_x"): + self.center = self.next_x, self.next_y + + def action_key_press_p(self, ev): + if ev.dict["key_name"] == "p": + self.push_event_local(gen_TRIAL()) + + def action_trIal(self, ev): + print ev.ev_name, ev.dict + + def action_instance_create(self, ev): + print "Instance created" + + +@register_class(fun_Game) +class wall(fun_Class): + sprite_defaults = { + "type": Rect, + "params": { + "size": (60, 40), + "color": None + } + } + +if __name__ == "__main__": + a = ball(0, 80) + b = wall(450, 60) + g.run() diff --git a/test_explicit_blit.pyc b/test_explicit_blit.pyc new file mode 100644 index 0000000..71eccdb Binary files /dev/null and b/test_explicit_blit.pyc differ diff --git a/test_register_event b/test_register_event new file mode 100644 index 0000000..c36e1d5 --- /dev/null +++ b/test_register_event @@ -0,0 +1,141 @@ +from utils import KW_EVENTS, fuzzy_match_event_name +from functools import partial +import logging + +logging.basicConfig(level=logging.DEBUG) + + +class fun_Class(object): + # dict_action_func -> dict of action funcs, key=event_numb like KEYDOWN + dict_action_func = {} + + @classmethod + def register_event(cls, event_name=None, func_to_register=None): + ''' + @fun_Class.register_event() + def event_key_down(ev): + pass + OR + @fun_Class.register_event(event_name) # event_name=KEYDOWN (or "key_down") + def whatever(ev): + pass + OR + fun_Class.register_event(event_name,func) + ''' + if hasattr(func_to_register, "im_class"): + print("im_class=") + print func_to_register.im_class,func_to_register.imelf, + if (event_name is None) and (func_to_register is None): + # i.e No param passed, used as decorator (1st step of ex. 1) + logging.info("Nothing passed") + return cls.register_event + elif (not(event_name is None)) and (func_to_register is None): + # i.e. one param is passed, maybe func or (str or int) + logging.info("Event Name only passed") + if isinstance(event_name, int) or isinstance(event_name, str): + # i.e. 1st parm-> str or int, is event name, acts as decorator + # (2nd ex.) + # dont hardcode func name- use metaclass + logging.info("Event Name is event_name") + return partial(cls.register_event, event_name) + elif hasattr(event_name, "__call__"): + # first param is func, actually func_to_register. + # So get event_name from func_name (2nd step of 1st ex.) + logging.info("Event Name is actually func") + func_to_register = event_name + event_name = fuzzy_match_event_name(func_to_register.func_name) + return cls.register_event(event_name, func_to_register) + else: + raise TypeError("event_name must be (str or int) or func") + else: + # Both params present + logging.info("Both param passed") + + if isinstance(event_name, str): + event_name = fuzzy_match_event_name(event_name) + event_numb = KW_EVENTS[event_name] + elif isinstance(event_name, int): + event_numb = event_name + else: + raise TypeError("event_name should be str or int") + + logging.info("Registering func") + if not (event_numb in cls.dict_action_func): + cls.dict_action_func[event_numb] = [] + cls.dict_action_func[event_numb].append(func_to_register) + + # logging.debug(func_to_register) + return func_to_register + + +class child(fun_Class): + pass + + +@child.register_event("key_down") +def ev_arbit(self): + pass +logging.info(fun_Class.dict_action_func) +logging.info(child.dict_action_func) + + +class child2(fun_Class): + + @fun_Class.register_event("key_down") + def ev_arbit_2(self): + pass +logging.info(fun_Class.dict_action_func) +logging.info(child2.dict_action_func) + +# Test 1 + +# logging.info("\nRunning Test 1") + + +# @fun_Class.register_event("KEydown") +# def ev_arbit(): +# pass +# logging.info(ev_arbit) + +# Test 1.1 + +# logging.info("\nRunning Test 1.1") + + +# @fun_Class.register_event("KEydown") +# def ev_arbit_2(): +# pass +# logging.info(fun_Class.dict_action_func) +# logging.info(ev_arbit_2) + +# Test 2 + +# logging.info("\nRunning Test 2") + + +# @fun_Class.register_event +# def ev_key_up(): +# pass +# logging.info(fun_Class.dict_action_func) +# logging.info(ev_key_up) + +# Test 3 +# logging.info("\nRunning Test 3") + + +# def test_mousemotion(): +# pass +# test_mousemotion = fun_Class.register_event(test_mousemotion) +# logging.info(fun_Class.dict_action_func) +# logging.info(test_mousemotion) + +# Test 4 + +# logging.info("\nRunning Test 4") + + +# def test_wat(): +# pass +# test_wat = fun_Class.register_event("mousemotion", test_wat) +# logging.info(fun_Class.dict_action_func) +# logging.info(test_wat) diff --git a/to_search.txt b/to_search.txt new file mode 100644 index 0000000..0ef1696 --- /dev/null +++ b/to_search.txt @@ -0,0 +1,33 @@ +how @classmethod and @property works +how to call in python shell for debugging (import pdb;pdb.set_trace()) +what are best-practises for writing classes that makes for better and flexible sub-classes +how __get__ works + +Learnt: +1. Can define class functions and class properties (getter/setter) using metaclass. + +IMP (Have to try): + +import inspect + +class meta(type): + def __new__(cls, name, base, clsdict): + temp_cls = type.__new__(cls, name, base, clsdict) + methods=inspect.getmembers(temp_cls,inspect.ismethod) + for method in methods: + print inspect.getargspec(method[1]) + +class A(Object): + __metaclass__=meta + @doit(what_to_do) + def abc(): + pass + +def doit(what_to_do, main_func): + def abc(what_to_do=what_to_do,main_func=main_func): + pass + return "pass" function containing both params (and a special param to indicate doit-processed func)\ + with default values set (default values are themself func). + +What meta does -> +Identify funcs processed with doit and return main_func after invoking what_to_do \ No newline at end of file diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..8b4f9f3 --- /dev/null +++ b/todo.txt @@ -0,0 +1,114 @@ +dx,dy for sprites +mouse.x,mouse.y +mouse_collision_events +is_focused func + +(do) room + (Done) move by wasd + (Done) resize about center by +- + (Done) ---- resize Working, but problem with ints, need internal floats + (Done) show all instances + add, delete button + (Done) highlight on focus (?) + (Done differently) make fake instances, not classes + (Done) Create from game.py + (Done) all prev actions with mouse + + (Done) export to json + ** save ori game.py filename** + (Done) save original size and other props, which should be restored while loading after populating loaded shapes + (Done) Load from json, needs seperate inst creation logic + () saved map cant depend upon original game.py, reduces portability + needs some sort of update from existing map method, think later + + Create map of certain dimension + ** extracted from game.py ** + + ** menu - load, new, export ** + -- fake pygame module for faster map loading -- + ** color conversion ** + -- Heavy code refactoring -- + ** grid snapping ** + +Serialize pygame.color + + LATER + transparent panels + +(think) timer + +(later) UI +panel-ish things where x,y is diff from whole screen +panels dynamically resizable +extra events for focus, mouse click, keyboard tabbing and value change stuff +e.g. to have a score sidebar + +(done for now) solve circular imports + +DOWNLOAD pyqt/pyside books +Todo: make set of excel funcs with pyxll +dwnload sp1, vb book and ide + +(drawing bug) with non-clearing rect aka contains condition + +Use rects retuned from blit to display.update +(imp) gen_event_IB filters - modify ev_name and dict, eg below +IMP - learn how import works +(later) actual bitmap collision. +(later) move collisions to sep. module + +INSTALL win32api and check boa +DOWNLOAD Editors such as wxDesigner, wxrcedit, XRCed and wxWorkshop + +(todo, add warning) pygame.locals has pygame.Rect. So, importing that may overload sprite_types.Rect. fix this in base.py +state machine +fix tiltedrect,img, line,ellipse,point,tri + +filters: +key_press - cnvrt "mod" to list, +mouse - cnvrt btn_id to btn_name, +mouse - mod get_btn_name to include 4 and 5 scroll ids. +look for more + +(imp) draw_ext +add TEXT class, open all shape drawing and rect filling api +(think) all in_place operators should return self, i.e. chainable +(think) use from_container_obj classmethod for all sprite creation and cnstrctr for standalone use +(think) move key and mouse funcs from event.py to seperate modules +(think) asset management +(think) views +(think) physics +(think) brython + +(think) use key_names instead of id, sort it out +(later) make fun_Game instances global +(later) make fun_Game instance singleton (??) or maybe needs no instantiation (seems better, everything class,ethod and classprop) +(later) some action plugins for common scenarios +(later) unregister classes + +(Done) distance and angle, motion_add funcs +(Done, but draw-update rect solves both) blit at int(x),int(y) +(Done) default action_close +(Done) top-level custom event generator working +(Done) event XXgeneratorXX / pushing at XX(class) [Not sure how to implement]XX and instance level +(done) img - implement collision_mask with existing shapes, +(done)mouse_press events +(done) fix check_event-ish func (soln- moved to event), also old key_press_states +(done) Fixed rect,circle. +(done) *explicit blit print always showing, when more than 1 obj +(done) *not implemented completely. +knowing about others(Done) +(done) more collision like GM +(Done) in-built hspeed, vspeed, direction, speed,accln, x_prev, y_prev, x_start, y_start +(Done) Circle(params).update for not still_valid old_rects -> optimize, maybe save bbox in list instead of creating for each old one +(Done) fix putting "still_valid" within dict_update used for marking (added note, was actually a good idea, Done) + + +why the heck 60fps flickering -> in-built recording mechanism? +forum discussion=> display.flip fixes it + +clear_rect+new_drawn_rect passed to update -> optimize? (for later, have to ask) + + + +make graphical glitches by removing few updates_display :D diff --git a/uniquelist.py b/uniquelist.py index 0561bab..12e456a 100644 --- a/uniquelist.py +++ b/uniquelist.py @@ -1,20 +1,32 @@ +# move it to utils.py +# also look at frozensets +# look at import collections - lot of good data structures +# learn about co-routines (returning generators from generators i.e. yield +# statement) + + class uniquelist(list): + ''' A list whose every value is unique. uniquelist.append(val) or __setitem__(pos,val) i.e. uniquelist[pos]=val is ignored if val is already in uniquelist. Returns True if the value is added, else returns False. ''' - def __setitem__(self,pos,val): + + def __setitem__(self, pos, val): if val not in self: - super(uniquelist,self).__setitem__(pos,val) + super(uniquelist, self).__setitem__(pos, val) return True else: return False - def append(self,val): + + def append(self, val): if val not in self: - super(uniquelist,self).append(val) + super(uniquelist, self).append(val) return True else: return False - def extend(self,iterable): - for temp in iterable: # todo: use yield so that it is not copied into memory + + def extend(self, iterable): + # todo: use yield so that it is not copied into memory + for temp in iterable: self.append(temp) - # Todo: Allow for type checking by allowing a type_of_members to be set \ No newline at end of file + # Todo: Allow for type checking by allowing a type_of_members to be set diff --git a/uniquelist.pyc b/uniquelist.pyc new file mode 100644 index 0000000..bc09d29 Binary files /dev/null and b/uniquelist.pyc differ diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..b05f99b --- /dev/null +++ b/utils.py @@ -0,0 +1,268 @@ +# Utility functions +# ToDo: Put everything in a class as staticmethods when imported, if +# name=__main__, keep them local +from functools import partial +import inspect +import logging +import types +import event +from uniquelist import uniquelist +from UncasedDict import UncasedDict + + +def register(list_to_register=None): + ''' Used as decorator: Adds the function (or anything else) to list_to_register ''' + def temp_func(what_to_register): + list_to_register.append(what_to_register) + return what_to_register + return temp_func + + +def fuzzy_match_event_name(string, event_or_action): + str_upper = string.upper() + if not (str_upper in event.dict_event_name): + event_or_action = event_or_action.lower() + if event_or_action.lower() == "event" or event_or_action.lower() == "action": + # STR_STARTER becomes event_ or action_ + STR_STARTER = event_or_action.upper() + "_" + else: + raise AttributeError( + "event_or_action arg must be 'event' or 'action'.") + str_normalized = str_upper[ + str_upper.find(STR_STARTER) + len(STR_STARTER):] + for tmp_event_name in event.dict_event_name: + # tmp event is the event_name, keys of KW_EVENTS + tmp_event_name = tmp_event_name.upper() + tmp_event_name_normalized = tmp_event_name.replace("_", "") + if str_normalized.startswith(tmp_event_name) or str_normalized.startswith(tmp_event_name_normalized): + return tmp_event_name + else: + return str_upper + + +class Action(object): + + """ pygame.event.Action(type, dict): return Action + pygame.Action.Action(type, **attributes): return Action + """ + + def __init__(self, type, *args, **kwargs): + super(Action, self).__init__() + self.type = type + _dict = {} + if len(args) == 1: + _dict.update(args[0]) + elif len(args) > 1: + raise TypeError( + "Max one non-keyword argument allowed (which is set to dict)") + if kwargs: + _dict.update(kwargs) + if _dict: + self.dict = _dict + + +def copy_func(f, name=None): + return types.FunctionType(f.func_code, f.func_globals, name or f.func_name, + f.func_defaults, f.func_closure) + + +def selfie_depreceated(f): + """Decorate function `f` to pass a reference to the function + as the first argument""" + return partial(f, f) + + +def selfie(f): + """Decorate function `f` to pass a reference to the function + as the first argument""" + return f.__get__(f, type(f)) + + +def sync_func(func1, func2): + '''Makes the 1st param (func) equivalent to 2nd param (func).''' + func1.func_code = func2.func_code + func1.func_defaults = func2.func_defaults + func1.func_dict = func2.func_dict + func1.func_doc = func2.func_doc + func1.func_name = func2.func_name + + +class meta_accessor_class(type): + + def __new__(cls, name, base, clsdict): + temp_cls = type.__new__(cls, name, base, clsdict) + temp_cls.list_instance = uniquelist() + temp_cls.list_check_func = uniquelist() + # dict_action_func -> dict of action funcs, key=event_numb like KEYDOWN + temp_cls.dict_action_func = UncasedDict() + methods = inspect.getmembers(temp_cls, inspect.ismethod) + for (method_name, method_obj) in methods: + if fuzzy_match_event_name(method_name, "action"): + temp_cls.register_action(method_obj) + return temp_cls + + +@selfie +def run_dec_on_meta(self, what_to_do): + '''Runs decorator (what_to_do) passed to it during meta with args, cls=>class and func=>function which this is used on''' + # make copies of this func and instead of params use .vars. Should accept + # *args,*kwargs to main_func. Next step: multiple funcs, think. + + def inter_run_dec_on_meta(*args, **kwargs): + # this intermediate func is used to produce func_process_meta for each + # time + def func_process_meta(self, *args, **kwargs): + if not hasattr(self, "_process_meta"): + # changing own's state, should be restored at last step before + # meta-return + self._process_meta = False + #(below) unneccesary here + if not hasattr(self, "__old_args") and not hasattr(self, "__old_kwargs"): + self.__old_args = None + self.__old_kwargs = None + if not self._process_meta: + # as long as not being meta-processed + # its not really main_func, really its just what_to_do's param + if (self.__old_args or self.__old_kwargs): + self.what_to_do = partial( + self.what_to_do, *(self.__old_args), **(self.__old_kwargs)) + self.__old_args = args + self.__old_kwargs = kwargs + return selfie(self) + else: + self.main_func = self.__old_args[0] + return self + + func_process_meta.__old_args = args + func_process_meta.__old_kwargs = kwargs + func_process_meta.what_to_do = what_to_do + func_process_meta.selfie = True + func_process_meta = selfie(func_process_meta) + return func_process_meta + return inter_run_dec_on_meta + + +def run_func_on_meta(run_func): + '''Runs func (run_func) passed to it during meta with arg, cls. + run_func remains a method of the class.''' + run_func._run_meta = True + return run_func + + +def register_action(event_name=None, func_to_register=None, cls=None): + ''' + @fun_Class.register_action() # @fun_Class.register_action === @fun_Class.register_action() + def event_key_down(ev): + pass + OR + # event_name=KEYDOWN (or "key_down") + @fun_Class.register_action(event_name) + def whatever(ev): + pass + OR + fun_Class.register_action(event_name,func) + ''' + if (event_name is None) and (func_to_register is None): + # i.e No param passed, used as decorator (1st step of ex. 1) + logging.info("Nothing passed") + return register_action + elif (not(event_name is None)) and (func_to_register is None): + # i.e. one param is passed, maybe func or (str or int) + logging.info("Event Name only passed") + if isinstance(event_name, str): + # i.e. 1st parm-> str , is event name, acts as decorator + # (2nd ex.) + # dont hardcode func name- use metaclass + logging.info("Event Name is event_name") + return partial(cls.register_action, event_name) + elif hasattr(event_name, "__call__"): + # first param is func, actually func_to_register. + # So get event_name from func_name (2nd step of 1st ex.) + logging.info("Event Name is actually func") + func_to_register = event_name + event_name = fuzzy_match_event_name( + func_to_register.func_name, "action") + return register_action(event_name=event_name, func_to_register=func_to_register, cls=cls) + else: + raise TypeError("event_name must be str or func") + else: + # Both params present + logging.info("Both param passed") + + if isinstance(event_name, str): + event_name = fuzzy_match_event_name(event_name, "action") + + logging.info("Registering func") + if not (event_name in cls.dict_action_func): + cls.dict_action_func[event_name] = [] + list_ = cls.dict_action_func[event_name] + if not (func_to_register in list_): + list_.append(func_to_register) + else: + raise Exception("func_to_register was already rgistered, skipping") + + return func_to_register + + +def register_class(game_class, cls=None): + ''' Register this class on mega_class. + Calls _regitser_class_ on mega_class with arg cls''' + if cls is None: + return partial(register_class, game_class) + game_class.register_class(cls) + cls.game_obj = game_class + return cls + + +def remove_from_list(list_, val, recursive=False): + if val in list_: + list_.remove(val) + if recursive: + while (val in list_): + list_.remove(val) + return list_ + + +def rect_to_pygame_rect(rect): + if hasattr(rect, "bbox"): + rect = rect.bbox + return rect + + +class updater_list(list): + + """list which also calls a updater func on data change""" + + def __init__(self, updater_func, *arg, **kwargs): + super(updater_list, self).__init__(*arg, **kwargs) + self.updater_func = updater_func + + def insert(self, *arg, **kwargs): + to_return = super(updater_list, self).insert(*arg, **kwargs) + self.updater_func(self) + return to_return + + def pop(self, *arg, **kwargs): + to_return = super(updater_list, self).pop(*arg, **kwargs) + self.updater_func(self) + return to_return + + def append(self, *arg, **kwargs): + to_return = super(updater_list, self).append(*arg, **kwargs) + self.updater_func(self) + return to_return + + def extend(self, *arg, **kwargs): + to_return = super(updater_list, self).extend(*arg, **kwargs) + self.updater_func(self) + return to_return + + def __getitem__(self, *arg, **kwargs): + to_return = super(updater_list, self).__getitem__(*arg, **kwargs) + self.updater_func(self) + return to_return + + def __setitem__(self, *arg, **kwargs): + to_return = super(updater_list, self).__setitem__(*arg, **kwargs) + self.updater_func(self) + return to_return diff --git a/utils.pyc b/utils.pyc new file mode 100644 index 0000000..796d104 Binary files /dev/null and b/utils.pyc differ diff --git a/utils_exp.py b/utils_exp.py new file mode 100644 index 0000000..1dc3e45 --- /dev/null +++ b/utils_exp.py @@ -0,0 +1,247 @@ +# Utility functions +# ToDo: Put everything in a class as staticmethods when imported, if +# name=__main__, keep them local +from pygame import locals as pygame_consts +from functools import partial +import inspect +import logging +import types + + +def register(list_to_register=None): + ''' Used as decorator: Adds the function (or anything else) to list_to_register ''' + def temp_func(what_to_register): + list_to_register.append(what_to_register) + return what_to_register + return temp_func + +KW_EVENTS = { + "QUIT": pygame_consts.QUIT, + "ACTIVE_EVENT": pygame_consts.ACTIVEEVENT, + "KEY_DOWN": pygame_consts.KEYDOWN, + "KEY_UP": pygame_consts.KEYUP, + "MOUSE_MOTION": pygame_consts.MOUSEMOTION, + "MOUSE_BUTTON_UP": pygame_consts.MOUSEBUTTONUP, + "MOUSE_BUTTON_DOWN": pygame_consts.MOUSEBUTTONDOWN, + "JOY_AXIS_MOTION": pygame_consts.JOYAXISMOTION, + "JOY_BALL_MOTION": pygame_consts.JOYBALLMOTION, + "JOY_HAT_MOTION": pygame_consts.JOYHATMOTION, + "JOY_BUTTON_UP": pygame_consts.JOYBUTTONUP, + "JOY_BUTTOND_OWN": pygame_consts.JOYBUTTONDOWN, + "VIDEO_RESIZE": pygame_consts.VIDEORESIZE, + "VIDEO_EXPOSE": pygame_consts.VIDEOEXPOSE, + "USER_EVENT": pygame_consts.USEREVENT, + # think of begin_step, step and end_step + "BEGIN_STEP": pygame_consts.USEREVENT + 1, + "STEP": pygame_consts.USEREVENT + 2, + "END_STEP": pygame_consts.USEREVENT + 3 +} + + +def fuzzy_match_event_name(string): +# print(string) + str_normalized = string.upper() + for tmp_event in KW_EVENTS: + # tmp event is the event_name, keys of KW_EVENTS + tmp_event_normalized = tmp_event.replace("_", "") + if (tmp_event in str_normalized) or (tmp_event_normalized in str_normalized): + return tmp_event + + +class Action(object): + + """ pygame.event.Action(type, dict): return Action + pygame.Action.Action(type, **attributes): return Action + """ + + def __init__(self, type, *args, **kwargs): + super(Action, self).__init__() + self.type = type + _dict = {} + if len(args) == 1: + _dict.update(args[0]) + elif len(args) > 1: + raise TypeError( + "Max one non-keyword argument allowed (which is set to dict)") + if kwargs: + _dict.update(kwargs) + if _dict: + self.dict = _dict + + +def copy_func(f, name=None): + return types.FunctionType(f.func_code, f.func_globals, name or f.func_name, + f.func_defaults, f.func_closure) + + +def selfie_depreceated(f): + """Decorate function `f` to pass a reference to the function + as the first argument""" + return partial(f, f) + + +def selfie(f): + """Decorate function `f` to pass a reference to the function + as the first argument""" + return f.__get__(f, type(f)) + + +def sync_func(func1, func2): + '''Makes the 1st param (func) equivalent to 2nd param (func).''' + func1.func_code = func2.func_code + func1.func_defaults = func2.func_defaults + func1.func_dict = func2.func_dict + func1.func_doc = func2.func_doc + func1.func_name = func2.func_name + + +class meta_accessor_class(type): + + ''' This metaclass allows class access to any function (or as extended use, to metaclasses)''' + # Using metaclass and decorator to allow class access during class creation time + # No method defined within the class should have "_process_meta" as arg + # Potential problems: Using closures, function.func_globals is read-only + # Doesnt get appropiate globals, maybe pass it (or the reqd global vars) + # as param on func defn + def __new__(cls, name, base, clsdict): + temp_cls = type.__new__(cls, name, base, clsdict) + methods = inspect.getmembers(temp_cls, inspect.ismethod) + for (method_name, method_obj) in methods: + has_im_func = hasattr(method_obj, "im_func") + has_func = hasattr(method_obj, "func") + while has_im_func or has_func: + if has_im_func: + method_obj = method_obj.im_func + if has_func: + # cater for partials + method_obj = method_obj.func + has_im_func = hasattr(method_obj, "im_func") + has_func = hasattr(method_obj, "func") + + if hasattr(method_obj, "_process_meta"): + method_obj._process_meta = True + # method_obj should return itself after setting main_func + if hasattr(method_obj,"selfie"): + method_obj = method_obj(method_obj) + else: + method_obj = method_obj() + what_to_do, main_func = method_obj.what_to_do, method_obj.main_func + # as we have resolved all im_funcs in + # partial,classmethod,boundmethods + f = method_obj + sync_func(f, main_func) + # func_to_register hardcoded + mod_func = what_to_do(f, cls=temp_cls) + sync_func(f, mod_func) + + return temp_cls + + +@selfie +def run_dec_on_meta(self, what_to_do): + '''Runs decorator (what_to_do) passed to it during meta with args, cls=>class and func=>function which this is used on''' + # must be deepcopied bcoz multiple versions will edit main func +# syntax error to grab attention + # make copies of this func and instead of params use .vars. Should accept + # *args,*kwargs to main_func. Next step: multiple funcs, think. + + def inter_run_dec_on_meta(*args,**kwargs): + # this intermediate func is used to produce func_process_meta for each time + def func_process_meta(self, *args, **kwargs): + if not hasattr(self, "_process_meta"): + # changing own's state, should be restored at last step before + # meta-return + self._process_meta = False + #(below) unneccesary here + if not hasattr(self,"__old_args") and not hasattr(self,"__old_kwargs"): + self.__old_args = None + self.__old_kwargs = None + if not self._process_meta: + # as long as not being meta-processed + # its not really main_func, really its just what_to_do's param + if (self.__old_args or self.__old_kwargs): + self.what_to_do = partial(self.what_to_do, *(self.__old_args), **(self.__old_kwargs)) + self.__old_args = args + self.__old_kwargs = kwargs + return selfie(self) + else: + self.main_func = self.__old_args[0] + return self + + func_process_meta.__old_args = args + func_process_meta.__old_kwargs = kwargs + func_process_meta.what_to_do = what_to_do + func_process_meta.selfie=True + func_process_meta = selfie(func_process_meta) + return func_process_meta + return inter_run_dec_on_meta + +def run_func_on_meta(run_func): + '''Runs func (run_func) passed to it during meta with arg, cls. + run_func remains a method of the class.''' + def what_to_do(func, cls): + func(cls=cls) + return func + return run_dec_on_meta(what_to_do=what_to_do, main_func=run_func) + + +def register_event(event_name=None, func_to_register=None, cls=None): + ''' + @fun_Class.register_event() # @fun_Class.register_event === @fun_Class.register_event() + def event_key_down(ev): + pass + OR + # event_name=KEYDOWN (or "key_down") + @fun_Class.register_event(event_name) + def whatever(ev): + pass + OR + fun_Class.register_event(event_name,func) + ''' + if (event_name is None) and (func_to_register is None): + # i.e No param passed, used as decorator (1st step of ex. 1) + logging.info("Nothing passed") + return register_event + elif (not(event_name is None)) and (func_to_register is None): + # i.e. one param is passed, maybe func or (str or int) + logging.info("Event Name only passed") + if isinstance(event_name, int) or isinstance(event_name, str): + # i.e. 1st parm-> str or int, is event name, acts as decorator + # (2nd ex.) + # dont hardcode func name- use metaclass + logging.info("Event Name is event_name") + return partial(cls.register_event, event_name) + elif hasattr(event_name, "__call__"): + # first param is func, actually func_to_register. + # So get event_name from func_name (2nd step of 1st ex.) + logging.info("Event Name is actually func") + func_to_register = event_name + event_name = fuzzy_match_event_name(func_to_register.func_name) + return register_event(event_name=event_name, func_to_register=func_to_register, cls=cls) + else: + raise TypeError("event_name must be (str or int) or func") + else: + # Both params present + logging.info("Both param passed") + + if isinstance(event_name, str): + event_name = fuzzy_match_event_name(event_name) + event_numb = KW_EVENTS[event_name] + elif isinstance(event_name, int): + event_numb = event_name + else: + raise TypeError("event_name should be str or int") + + logging.info("Registering func") + if not (event_numb in cls.dict_action_func): + cls.dict_action_func[event_numb] = [] + cls.dict_action_func[event_numb].append(func_to_register) + + return func_to_register + + + +##_register_event_ = register_event +##del register_event +# so that register event is actually delegated register +##register_event = run_dec_on_meta(_register_event_) diff --git a/utils_exp.pyc b/utils_exp.pyc new file mode 100644 index 0000000..f815b04 Binary files /dev/null and b/utils_exp.pyc differ diff --git a/utils_old.py b/utils_old.py new file mode 100644 index 0000000..3fb5aad --- /dev/null +++ b/utils_old.py @@ -0,0 +1,322 @@ +# Utility functions +# ToDo: Put everything in a class as staticmethods when imported, if +# name=__main__, keep them local +from pygame import locals as pygame_consts +from functools import partial +import inspect +import logging +import types +import pygame +from uniquelist import uniquelist + + +def register(list_to_register=None): + ''' Used as decorator: Adds the function (or anything else) to list_to_register ''' + def temp_func(what_to_register): + list_to_register.append(what_to_register) + return what_to_register + return temp_func + +KW_EVENTS_IB = { + "QUIT": pygame_consts.QUIT, + "ACTIVE_EVENT": pygame_consts.ACTIVEEVENT, + "KEY_DOWN": pygame_consts.KEYDOWN, + "KEY_UP": pygame_consts.KEYUP, + "MOUSE_MOTION": pygame_consts.MOUSEMOTION, + "MOUSE_BUTTON_UP": pygame_consts.MOUSEBUTTONUP, + "MOUSE_BUTTON_DOWN": pygame_consts.MOUSEBUTTONDOWN, + "JOY_AXIS_MOTION": pygame_consts.JOYAXISMOTION, + "JOY_BALL_MOTION": pygame_consts.JOYBALLMOTION, + "JOY_HAT_MOTION": pygame_consts.JOYHATMOTION, + "JOY_BUTTON_UP": pygame_consts.JOYBUTTONUP, + "JOY_BUTTOND_OWN": pygame_consts.JOYBUTTONDOWN, + "VIDEO_RESIZE": pygame_consts.VIDEORESIZE, + "VIDEO_EXPOSE": pygame_consts.VIDEOEXPOSE, + # "USER_EVENT": pygame_consts.USEREVENT, + # think of begin_step, step and end_step +} + +# Make kw_events_user automatically + +LIST_EVENTS_USER = ["BEGIN_STEP", "STEP", "DRAW", "END_STEP"] + +KW_EVENTS_USER = {} +KW_EVENTS_ALL = {} + + +def populate_KW_EVENTS_USER(LIST_EVENTS_USER=LIST_EVENTS_USER): + '''Populates KW_EVENTS_USER and also updates KW_EVENTS_ALL''' + tmp_event_numb = pygame_consts.USEREVENT + for tmp_event_name in LIST_EVENTS_USER: + KW_EVENTS_USER[tmp_event_name] = tmp_event_numb + tmp_event_numb += 1 + global KW_EVENTS_ALL + KW_EVENTS_ALL = {} + for tmp_event_name in KW_EVENTS_IB: + KW_EVENTS_ALL[tmp_event_name] = KW_EVENTS_IB[tmp_event_name] + for tmp_event_name in KW_EVENTS_USER: + KW_EVENTS_ALL[tmp_event_name] = KW_EVENTS_USER[tmp_event_name] +# populated by default, may be repopulated later +populate_KW_EVENTS_USER() + +CUE_EVENTS_IB = "unordered IB events here" +LIST_EVENTS_ORDERED = [KW_EVENTS_USER["BEGIN_STEP"], CUE_EVENTS_IB, KW_EVENTS_USER["STEP"], + KW_EVENTS_USER["DRAW"], KW_EVENTS_USER["END_STEP"]] + + +def sort_events(ev1, ev2): + ev_args = [ev1, ev2] + args_pos = [None, None] + # fix next line + for tmp_index in range(len(ev_args)): + tmp_ev = ev_args[tmp_index] + if (tmp_ev.type in KW_EVENTS_IB.values()): + args_pos[tmp_index] = LIST_EVENTS_ORDERED.index(CUE_EVENTS_IB) + elif (tmp_ev.type in KW_EVENTS_USER.values()): + args_pos[tmp_index] = LIST_EVENTS_ORDERED.index(tmp_ev.type) + tmp_diff = args_pos[0] - args_pos[1] + # convert -ve to -1, 0 to 0, +ve to +1 + if tmp_diff == 0: + tmp_diff_unit = 0 + else: + tmp_diff_unit = tmp_diff / abs(tmp_diff) + return tmp_diff_unit + + +def append_user_events(list_): + for temp_ev_name in LIST_EVENTS_USER: + temp_ev_numb = KW_EVENTS_USER[temp_ev_name] + list_.append(pygame.event.Event(temp_ev_numb)) + return list_ + + +def fuzzy_match_event_name(string, event_or_action): + str_upper = string.upper() + if not (str_upper in KW_EVENTS_ALL): + event_or_action = event_or_action.lower() + if event_or_action.lower() == "event" or event_or_action.lower() == "action": + # STR_STARTER becomes event_ or action_ + STR_STARTER = event_or_action.upper() + "_" + else: + raise AttributeError( + "event_or_action arg must be 'event' or 'action'.") + str_normalized = str_upper[ + str_upper.find(STR_STARTER) + len(STR_STARTER):] + for tmp_event_name in KW_EVENTS_ALL: + # tmp event is the event_name, keys of KW_EVENTS + tmp_event_name_normalized = tmp_event_name.replace("_", "") + if str_normalized.startswith(tmp_event_name) or str_normalized.startswith(tmp_event_name_normalized): + return tmp_event_name + else: + return str_upper + + +class Action(object): + + """ pygame.event.Action(type, dict): return Action + pygame.Action.Action(type, **attributes): return Action + """ + + def __init__(self, type, *args, **kwargs): + super(Action, self).__init__() + self.type = type + _dict = {} + if len(args) == 1: + _dict.update(args[0]) + elif len(args) > 1: + raise TypeError( + "Max one non-keyword argument allowed (which is set to dict)") + if kwargs: + _dict.update(kwargs) + if _dict: + self.dict = _dict + + +def copy_func(f, name=None): + return types.FunctionType(f.func_code, f.func_globals, name or f.func_name, + f.func_defaults, f.func_closure) + + +def selfie_depreceated(f): + """Decorate function `f` to pass a reference to the function + as the first argument""" + return partial(f, f) + + +def selfie(f): + """Decorate function `f` to pass a reference to the function + as the first argument""" + return f.__get__(f, type(f)) + + +def sync_func(func1, func2): + '''Makes the 1st param (func) equivalent to 2nd param (func).''' + func1.func_code = func2.func_code + func1.func_defaults = func2.func_defaults + func1.func_dict = func2.func_dict + func1.func_doc = func2.func_doc + func1.func_name = func2.func_name + + +class meta_accessor_class(type): + + ''' This metaclass allows class access to any function (or as extended use, to metaclasses)''' + # Using metaclass and decorator to allow class access during class creation time + # No method defined within the class should have "_process_meta" as arg + # Potential problems: Using closures, function.func_globals is read-only + # Doesnt get appropiate globals, maybe pass it (or the reqd global vars) + # as param on func defn + def __new__(cls, name, base, clsdict): + temp_cls = type.__new__(cls, name, base, clsdict) + temp_cls.list_instance = uniquelist() + temp_cls.list_check_func = uniquelist() + # dict_action_func -> dict of action funcs, key=event_numb like KEYDOWN + temp_cls.dict_action_func = {} + methods = inspect.getmembers(temp_cls, inspect.ismethod) + for (method_name, method_obj) in methods: + if fuzzy_match_event_name(method_name, "action"): + temp_cls.register_event(method_obj) + return temp_cls + + +@selfie +def run_dec_on_meta(self, what_to_do): + '''Runs decorator (what_to_do) passed to it during meta with args, cls=>class and func=>function which this is used on''' + # make copies of this func and instead of params use .vars. Should accept + # *args,*kwargs to main_func. Next step: multiple funcs, think. + + def inter_run_dec_on_meta(*args, **kwargs): + # this intermediate func is used to produce func_process_meta for each + # time + def func_process_meta(self, *args, **kwargs): + if not hasattr(self, "_process_meta"): + # changing own's state, should be restored at last step before + # meta-return + self._process_meta = False + #(below) unneccesary here + if not hasattr(self, "__old_args") and not hasattr(self, "__old_kwargs"): + self.__old_args = None + self.__old_kwargs = None + if not self._process_meta: + # as long as not being meta-processed + # its not really main_func, really its just what_to_do's param + if (self.__old_args or self.__old_kwargs): + self.what_to_do = partial( + self.what_to_do, *(self.__old_args), **(self.__old_kwargs)) + self.__old_args = args + self.__old_kwargs = kwargs + return selfie(self) + else: + self.main_func = self.__old_args[0] + return self + + func_process_meta.__old_args = args + func_process_meta.__old_kwargs = kwargs + func_process_meta.what_to_do = what_to_do + func_process_meta.selfie = True + func_process_meta = selfie(func_process_meta) + return func_process_meta + return inter_run_dec_on_meta + + +def run_func_on_meta(run_func): + '''Runs func (run_func) passed to it during meta with arg, cls. + run_func remains a method of the class.''' + run_func._run_meta = True + return run_func + + +def register_event(event_name=None, func_to_register=None, cls=None): + ''' + @fun_Class.register_event() # @fun_Class.register_event === @fun_Class.register_event() + def event_key_down(ev): + pass + OR + # event_name=KEYDOWN (or "key_down") + @fun_Class.register_event(event_name) + def whatever(ev): + pass + OR + fun_Class.register_event(event_name,func) + ''' + if (event_name is None) and (func_to_register is None): + # i.e No param passed, used as decorator (1st step of ex. 1) + logging.info("Nothing passed") + return register_event + elif (not(event_name is None)) and (func_to_register is None): + # i.e. one param is passed, maybe func or (str or int) + logging.info("Event Name only passed") + if isinstance(event_name, str): + # i.e. 1st parm-> str , is event name, acts as decorator + # (2nd ex.) + # dont hardcode func name- use metaclass + logging.info("Event Name is event_name") + return partial(cls.register_event, event_name) + elif hasattr(event_name, "__call__"): + # first param is func, actually func_to_register. + # So get event_name from func_name (2nd step of 1st ex.) + logging.info("Event Name is actually func") + func_to_register = event_name + event_name = fuzzy_match_event_name( + func_to_register.func_name, "action") + return register_event(event_name=event_name, func_to_register=func_to_register, cls=cls) + else: + raise TypeError("event_name must be str or func") + else: + # Both params present + logging.info("Both param passed") + + if isinstance(event_name, str): + event_name = fuzzy_match_event_name(event_name, "action") + + logging.info("Registering func") + if not (event_name in cls.dict_action_func): + cls.dict_action_func[event_name] = [] + cls.dict_action_func[event_name].append(func_to_register) + + return func_to_register + + +def register_class(game_class, cls=None): + ''' Register this class on mega_class. + Calls _regitser_class_ on mega_class with arg cls''' + if cls is None: + return partial(register_class, game_class) + game_class.register_class(cls) + cls.Game = game_class + return cls + + +def remove_from_list(list_, val, recursive=False): + if val in list_: + list_.remove(val) + if recursive: + while (val in list_): + list_.remove(val) + return list_ + + +def rect_to_pygame_rect(rect): + if hasattr(rect, "bbox"): + rect = rect.bbox + return rect + + +# _register_event_ = register_event +# del register_event +# so that register event is actually delegated register +# register_event = run_dec_on_meta(_register_event_) + +# Move to test +##import pygame +# pygame.init() +##evs = [] +# evs.append(pygame.event.Event(KW_EVENTS_IB["KEY_DOWN"])) +# evs.append(pygame.event.Event(KW_EVENTS_IB["JOY_AXIS_MOTION"])) +# evs.append(pygame.event.Event(KW_EVENTS_IB["KEY_UP"])) +# evs.append(pygame.event.Event(KW_EVENTS_IB["MOUSE_BUTTON_UP"])) +# evs.append(pygame.event.Event(KW_EVENTS_USER["DRAW"])) +# evs.append(pygame.event.Event(KW_EVENTS_USER["BEGIN_STEP"])) +# evs.append(pygame.event.Event(KW_EVENTS_USER["END_STEP"])) +# evs.append(pygame.event.Event(KW_EVENTS_USER["STEP"])) +# evs.sort(sort_events) diff --git a/what_i_see.png b/what_i_see.png new file mode 100644 index 0000000..7f4a108 Binary files /dev/null and b/what_i_see.png differ