Legend of the Gold Box... A game written for the LOWREZJAM 2018 game jam
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

368 lines
11KB

  1. '''
  2. Filename nodes.py
  3. Author: Bryan "ObsidianBlk" Miller
  4. Date Created: 8/1/2018
  5. Python Version: 3.7
  6. '''
  7. from .display import Display
  8. import pygame
  9. class NodeError(Exception):
  10. pass
  11. class Node:
  12. def __init__(self, name="Node", parent=None):
  13. self._parent = None
  14. self._name = name
  15. self._children = []
  16. if parent is not None:
  17. try:
  18. self.parent = parent
  19. except NodeError as e:
  20. raise e
  21. @property
  22. def parent(self):
  23. return self._parent
  24. @parent.setter
  25. def parent(self, new_parent):
  26. try:
  27. self.parent_to_node(new_parent)
  28. except NodeError as e:
  29. raise e
  30. @property
  31. def root(self):
  32. if self._parent is None:
  33. return self
  34. return self._parent.root
  35. @property
  36. def name(self):
  37. return self._name
  38. @name.setter
  39. def name(self, value):
  40. if self._parent is not None:
  41. if self._parent.get_node(value) is not None:
  42. raise NodeError("Parent already contains node named '{}'.".format(name))
  43. self._name = value
  44. @property
  45. def full_name(self):
  46. if self._parent is None:
  47. return self._name
  48. return self._parent.full_name + "." + self._name
  49. @property
  50. def child_count(self):
  51. return len(this._children)
  52. def parent_to_node(self, parent, allow_reparenting=False):
  53. if not isinstance(value, Node):
  54. raise NodeError("Node may only parent to another Node instance.")
  55. if self._parent is None or self._parent != parent:
  56. if self._parent is not None:
  57. if allow_Reparenting == False:
  58. raise NodeError("Node already assigned a parent Node.")
  59. if self._parent.remove_node(self) != self:
  60. raise NodeError("Failed to remove self from current parent.")
  61. try:
  62. parent.attach_node(self)
  63. except NodeError as e:
  64. raise e
  65. def attach_node(self, node, reparent=False, index=-1):
  66. if node.parent is not None:
  67. if node.parent == self:
  68. return # Nothing to do. Given node already parented to this node.
  69. if reparent == False:
  70. raise NodeError("Node already parented.")
  71. if node.parent.remove_node(node) != node:
  72. raise NodeError("Failed to remove given node from it's current parent.")
  73. if self.get_node(node.name) is not None:
  74. raise NodeError("Node with name '{}' already attached.".format(node.name))
  75. node._parent = self
  76. if index < 0 or index >= len(self._children):
  77. self._children.append(node)
  78. else:
  79. self._children.insert(index, node)
  80. def remove_node(self, node):
  81. if isinstance(node, (str, unicode)):
  82. n = self.get_node(node)
  83. if n is not None:
  84. try:
  85. return self.remove_node(n)
  86. except NodeError as e:
  87. raise e
  88. elif isinstance(node, Node):
  89. if node.parent != self:
  90. if node.parent == None:
  91. raise NodeError("Cannot remove an unparented node.")
  92. try:
  93. return node.parent.remove_node(node)
  94. except NodeError as e:
  95. raise e
  96. if node in self._children:
  97. self._children.remove(node)
  98. node._parent = None
  99. return node
  100. else:
  101. raise NodeError("Expected a Node instance or a string.")
  102. return None
  103. def get_node(self, name):
  104. if len(self._children) <= 0:
  105. return None
  106. subnames = name.split(".")
  107. for c in self._children:
  108. if c.name == subnames[0]:
  109. if len(subnames) > 1:
  110. return c.get_node(".".join(subnames[1:-1]))
  111. return c
  112. return None
  113. def _update(self, dt):
  114. if hasattr(self, "on_update"):
  115. self.on_update(dt)
  116. for c in self._children:
  117. c._update(dt)
  118. def _render(self, surface):
  119. for c in self._children:
  120. c._render(surface)
  121. class Node2D(Node):
  122. def __init__(self, name="Node2D", parent=None):
  123. try:
  124. Node.__init__(self, name, parent)
  125. except NodeError as e:
  126. raise e
  127. def _render(self, surface):
  128. Node._render(self, surface)
  129. if hasattr(self, "on_render"):
  130. self._ACTIVE_SURF = surface
  131. self.on_render()
  132. del self._ACTIVE_SURF
  133. def blit(self, img, pos=(0,0), rect=None):
  134. if not hasattr(self, "_ACTIVE_SURF"):
  135. return
  136. self._ACTIVE_SURF.blit(img, pos, rect)
  137. def fill(self, color):
  138. if not hasattr(self, "_ACTIVE_SURF"):
  139. return
  140. self._ACTIVE_SURF.fill(color)
  141. def draw_lines(self, points, color, thickness=1, closed=False):
  142. if not hasattr(self, "_ACTIVE_SURF"):
  143. return
  144. pygame.draw.lines(self._ACTIVE_SURF, color, closed, points, thickness)
  145. def draw_rect(self, rect, color, thickness=1):
  146. if not hasattr(self, "_ACTIVE_SURF"):
  147. return
  148. pygame.draw.rect(self._ACTIVE_SURF, color, rect, thickness)
  149. def draw_ellipse(self, rect, color, thickness=1, fill_color=None):
  150. if not hasattr(self, "_ACTIVE_SURF"):
  151. return
  152. if fill_color is not None:
  153. pygame.draw.ellipse(self._ACTIVE_SURF, fill_color, rect)
  154. if thickness > 0:
  155. pygame.draw.ellipse(self._ACTIVE_SURF, color, rect, thickness)
  156. def draw_circle(self, pos, radius, color, thickness=1, fill_color=None):
  157. if not hasattr(self, "_ACTIVE_SURF"):
  158. return
  159. if fill_color is not None:
  160. pygame.draw.circle(self._ACTIVE_SURF, fill_color, pos, radius)
  161. if thickness > 0:
  162. pygame.draw.circle(self._ACTIVE_SURF, color, pos, radius, thickness)
  163. def draw_polygon(self, points, color, thickness=1, fill_color=None):
  164. if not hasattr(self, "_ACTIVE_SURF"):
  165. return
  166. if fill_color is not None:
  167. pygame.draw.polygon(self._ACTIVE_SURF, fill_color, points)
  168. if thickness >= 1:
  169. pygame.draw.polygon(self._ACTIVE_SURF, color, points, thickness)
  170. class NodeSurface(Node2D):
  171. def __init__(self, name="NodeSurface", parent=None):
  172. try:
  173. Node2D.__init__(self, name, parent)
  174. except NodeError as e:
  175. raise e
  176. self._offset = (0.0, 0.0)
  177. self._scale = (1.0, 1.0)
  178. self._keepAspectRatio = False
  179. self._surface = None
  180. self._tsurface = None
  181. self.set_surface()
  182. def _updateTransformSurface(self):
  183. if self._surface is None:
  184. return
  185. if self._scale[0] == 1.0 and self._scale[1] == 1.0:
  186. self._tsurface = None
  187. return
  188. size = self._surface.get_size()
  189. nw = size[0] * self._scale[0]
  190. nh = 0
  191. if self._keepAspectRatio:
  192. nh = size[1] * self._scale[0]
  193. else:
  194. nh = size[1] * self._scale[1]
  195. self._tsurface = pygame.Surface((nw, nh), pygame.SRCALPHA, self._surface)
  196. self._tsurface.fill(pygame.Color(0,0,0,0))
  197. @property
  198. def resolution(self):
  199. if self._surface is None:
  200. return (0,0)
  201. return self._surface.get_size()
  202. @resolution.setter
  203. def resolution(self, res):
  204. try:
  205. self.set_surface(res)
  206. except (TypeError, ValueError) as e:
  207. raise e
  208. @property
  209. def width(self):
  210. return self.resolution[0]
  211. @property
  212. def height(self):
  213. return self.resolution[1]
  214. @property
  215. def offset(self):
  216. return self._offset
  217. @offset.setter
  218. def offset(self, offset):
  219. if not isinstance(offset, tuple):
  220. raise TypeError("Expected a tuple")
  221. if len(offset) != 2:
  222. raise ValueError("Expected tuple of length two.")
  223. if not isinstance(offset[0], (int, float)) or not isinstance(offset[1], (int, float)):
  224. raise TypeError("Expected number values.")
  225. self._offset = (float(offset[0]), float(offset[1]))
  226. @property
  227. def offset_x(self):
  228. return self._offset[0]
  229. @offset_x.setter
  230. def offset_x(self, x):
  231. if not isinstance(x, (int, float)):
  232. raise TypeError("Expected number value.")
  233. self._offset = (x, self._offset[1])
  234. @property
  235. def offset_y(self):
  236. return self._offset[1]
  237. @offset_y.setter
  238. def offset_y(self, y):
  239. if not isinstance(y, (int, float)):
  240. raise TypeError("Expected number value.")
  241. self._offset = (self._offset[0], y)
  242. @property
  243. def scale(self):
  244. return self._scale
  245. @scale.setter
  246. def scale(self, scale):
  247. if self._keepAspectRatio:
  248. if not isinstance(scale, (int, float)):
  249. raise TypeError("Expected number value.")
  250. self._scale = (scale, self._scale[1])
  251. else:
  252. if not isinstance(scale, tuple):
  253. raise TypeError("Expected a tuple")
  254. if len(scale) != 2:
  255. raise ValueError("Expected tuple of length two.")
  256. if not isinstance(scale[0], (int, float)) or not isinstance(scale[1], (int, float)):
  257. raise TypeError("Expected number values.")
  258. self._scale = scale
  259. self._updateTransformSurface()
  260. @property
  261. def keep_aspect_ratio(self):
  262. return self._keepAspectRatio
  263. @keep_aspect_ratio.setter
  264. def keep_aspect_ratio(self, keep):
  265. self._keepAspectRatio = (keep == True)
  266. self._updateTransformSurface()
  267. def scale_to(self, target_resolution):
  268. if self._surface is not None:
  269. size = self._surface.get_size()
  270. nscale = (float(size[0]) / float(target_resolution[0]), float(size[1]) / float(target_resolution[1]))
  271. self.scale = nscale
  272. def set_surface(self, resolution=None):
  273. dsurf = Display.surface
  274. if resolution is None:
  275. if dsurf is not None:
  276. self._surface = dsurf.convert_alpha()
  277. self._surface.fill(pygame.Color(0,0,0,0))
  278. self._updateTransformSurface()
  279. else:
  280. if not isinstance(r, tuple):
  281. raise TypeError("Expected a tuple.")
  282. if len(r) != 2:
  283. raise ValueError("Expected a tuple of length two.")
  284. if not isinstance(r[0], int) or not isinstance(r[1], int):
  285. raise TypeError("Tuple expected to contain integers.")
  286. if dsurf is not None:
  287. self._surface = pygame.Surface(resolution, pygame.SRCALPHA, dsurf)
  288. else:
  289. self._surface = pygame.Surface(resolution, pygame.SRCALPHA)
  290. self._surface.fill(pygame.Color(0,0,0,0))
  291. self._updateTransformSurface()
  292. def _render(self, surface):
  293. if self._surface is None:
  294. self.set_surface()
  295. if self._surface is not None:
  296. Node2D._render(self, self._surface)
  297. else:
  298. Node2D._render(self, surface)
  299. self._scale_and_blit(surface)
  300. def _scale_and_blit(self, dest):
  301. dsize = dest.get_size()
  302. pos = (int(self._offset[0]), int(self._offset[1]))
  303. src = self._surface
  304. if self._tsurface is not None:
  305. pygame.transform.scale(self._surface, self._tsurface.get_size(), self._tsurface)
  306. src = self._tsurface
  307. dest.blit(src, pos)