Legend of the Gold Box... A game written for the LOWREZJAM 2018 game jam
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

nodes.py 13KB

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