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.

541 lines
17KB

  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. from .resource import Manager
  10. import pygame
  11. class NodeError(Exception):
  12. pass
  13. class Node:
  14. def __init__(self, name="Node", parent=None):
  15. self._NODE_DATA={
  16. "parent":None,
  17. "name":name,
  18. "children":[]
  19. "resource":None
  20. }
  21. if parent is not None:
  22. try:
  23. self.parent = parent
  24. except NodeError as e:
  25. raise e
  26. @property
  27. def parent(self):
  28. return self._NODE_DATA["parent"]
  29. @parent.setter
  30. def parent(self, new_parent):
  31. try:
  32. self.parent_to_node(new_parent)
  33. except NodeError as e:
  34. raise e
  35. @property
  36. def root(self):
  37. if self.parent is None:
  38. return self
  39. return self.parent.root
  40. @property
  41. def name(self):
  42. return self._NODE_DATA["name"]
  43. @name.setter
  44. def name(self, value):
  45. if self.parent is not None:
  46. if self.parent.get_node(value) is not None:
  47. raise NodeError("Parent already contains node named '{}'.".format(name))
  48. self._NODE_DATA["name"] = value
  49. @property
  50. def full_name(self):
  51. if self.parent is None:
  52. return self.name
  53. return self.parent.full_name + "." + self.name
  54. @property
  55. def child_count(self):
  56. return len(this._NODE_DATA["children"])
  57. def parent_to_node(self, parent, allow_reparenting=False):
  58. if not isinstance(value, Node):
  59. raise NodeError("Node may only parent to another Node instance.")
  60. if self.parent is None or self.parent != parent:
  61. if self.parent is not None:
  62. if allow_Reparenting == False:
  63. raise NodeError("Node already assigned a parent Node.")
  64. if self.parent.remove_node(self) != self:
  65. raise NodeError("Failed to remove self from current parent.")
  66. try:
  67. parent.attach_node(self)
  68. except NodeError as e:
  69. raise e
  70. def attach_node(self, node, reparent=False, index=-1):
  71. if node.parent is not None:
  72. if node.parent == self:
  73. return # Nothing to do. Given node already parented to this node.
  74. if reparent == False:
  75. raise NodeError("Node already parented.")
  76. if node.parent.remove_node(node) != node:
  77. raise NodeError("Failed to remove given node from it's current parent.")
  78. if self.get_node(node.name) is not None:
  79. raise NodeError("Node with name '{}' already attached.".format(node.name))
  80. node._NODE_DATA["parent"] = self
  81. children = self._NODE_DATA["children"]
  82. if index < 0 or index >= len(children):
  83. children.append(node)
  84. else:
  85. children.insert(index, node)
  86. def remove_node(self, node):
  87. if isinstance(node, (str, unicode)):
  88. n = self.get_node(node)
  89. if n is not None:
  90. try:
  91. return self.remove_node(n)
  92. except NodeError as e:
  93. raise e
  94. elif isinstance(node, Node):
  95. if node.parent != self:
  96. if node.parent == None:
  97. raise NodeError("Cannot remove an unparented node.")
  98. try:
  99. return node.parent.remove_node(node)
  100. except NodeError as e:
  101. raise e
  102. if node in self._NODE_DATA["children"]:
  103. self._NODE_DATA["children"].remove(node)
  104. node._NODE_DATA["parent"] = None
  105. return node
  106. else:
  107. raise NodeError("Expected a Node instance or a string.")
  108. return None
  109. def get_node(self, name):
  110. if self.child_count <= 0:
  111. return None
  112. subnames = name.split(".")
  113. for c in self._NODE_DATA["children"]:
  114. if c.name == subnames[0]:
  115. if len(subnames) > 1:
  116. return c.get_node(".".join(subnames[1:-1]))
  117. return c
  118. return None
  119. def _update(self, dt):
  120. if hasattr(self, "on_update"):
  121. self.on_update(dt)
  122. for c in self._NODE_DATA["children"]:
  123. c._update(dt)
  124. def _render(self, surface):
  125. for c in self._NODE_DATA["children"]:
  126. c._render(surface)
  127. class Node2D(Node):
  128. def __init__(self, name="Node2D", parent=None):
  129. try:
  130. Node.__init__(self, name, parent)
  131. except NodeError as e:
  132. raise e
  133. def _render(self, surface):
  134. Node._render(self, surface)
  135. if hasattr(self, "on_render"):
  136. self._ACTIVE_SURF = surface
  137. self.on_render()
  138. del self._ACTIVE_SURF
  139. def draw_image(self, img, pos=(0,0), rect=None):
  140. if not hasattr(self, "_ACTIVE_SURF"):
  141. return
  142. self._ACTIVE_SURF.blit(img, pos, rect)
  143. def fill(self, color):
  144. if not hasattr(self, "_ACTIVE_SURF"):
  145. return
  146. self._ACTIVE_SURF.fill(color)
  147. def draw_lines(self, points, color, thickness=1, closed=False):
  148. if not hasattr(self, "_ACTIVE_SURF"):
  149. return
  150. pygame.draw.lines(self._ACTIVE_SURF, color, closed, points, thickness)
  151. def draw_rect(self, rect, color, thickness=1):
  152. if not hasattr(self, "_ACTIVE_SURF"):
  153. return
  154. pygame.draw.rect(self._ACTIVE_SURF, color, rect, thickness)
  155. def draw_ellipse(self, rect, color, thickness=1, fill_color=None):
  156. if not hasattr(self, "_ACTIVE_SURF"):
  157. return
  158. if fill_color is not None:
  159. pygame.draw.ellipse(self._ACTIVE_SURF, fill_color, rect)
  160. if thickness > 0:
  161. pygame.draw.ellipse(self._ACTIVE_SURF, color, rect, thickness)
  162. def draw_circle(self, pos, radius, color, thickness=1, fill_color=None):
  163. if not hasattr(self, "_ACTIVE_SURF"):
  164. return
  165. if fill_color is not None:
  166. pygame.draw.circle(self._ACTIVE_SURF, fill_color, pos, radius)
  167. if thickness > 0:
  168. pygame.draw.circle(self._ACTIVE_SURF, color, pos, radius, thickness)
  169. def draw_polygon(self, points, color, thickness=1, fill_color=None):
  170. if not hasattr(self, "_ACTIVE_SURF"):
  171. return
  172. if fill_color is not None:
  173. pygame.draw.polygon(self._ACTIVE_SURF, fill_color, points)
  174. if thickness >= 1:
  175. pygame.draw.polygon(self._ACTIVE_SURF, color, points, thickness)
  176. class NodeSurface(Node2D):
  177. def __init__(self, name="NodeSurface", parent=None):
  178. try:
  179. Node2D.__init__(self, name, parent)
  180. except NodeError as e:
  181. raise e
  182. # TODO: Update this class to use the _NODE*_DATA={} structure.
  183. self._offset = (0.0, 0.0)
  184. self._scale = (1.0, 1.0)
  185. self._scaleToDisplay = False
  186. self._scaleDirty = False
  187. self._keepAspectRatio = False
  188. self._alignCenter = False
  189. self._surface = None
  190. self._tsurface = None
  191. self.set_surface()
  192. def _updateTransformSurface(self):
  193. if self._surface is None:
  194. return
  195. self._scaleDirty = False
  196. if self._scaleToDisplay:
  197. dsize = Display.resolution
  198. ssize = self._surface.get_size()
  199. self._scale = (dsize[0] / ssize[0], dsize[1] / ssize[1])
  200. if self._keepAspectRatio:
  201. if self._scale[0] < self._scale[1]:
  202. self._scale = (self._scale[0], self._scale[0])
  203. else:
  204. self._scale = (self._scale[1], self._scale[1])
  205. if self._scale[0] == 1.0 and self._scale[1] == 1.0:
  206. self._tsurface = None
  207. return
  208. size = self._surface.get_size()
  209. nw = size[0] * self._scale[0]
  210. nh = 0
  211. if self._keepAspectRatio:
  212. nh = size[1] * self._scale[0]
  213. else:
  214. nh = size[1] * self._scale[1]
  215. self._tsurface = pygame.Surface((nw, nh), pygame.SRCALPHA, self._surface)
  216. self._tsurface.fill(pygame.Color(0,0,0,0))
  217. @property
  218. def resolution(self):
  219. if self._surface is None:
  220. return (0,0)
  221. return self._surface.get_size()
  222. @resolution.setter
  223. def resolution(self, res):
  224. try:
  225. self.set_surface(res)
  226. except (TypeError, ValueError) as e:
  227. raise e
  228. @property
  229. def width(self):
  230. return self.resolution[0]
  231. @property
  232. def height(self):
  233. return self.resolution[1]
  234. @property
  235. def offset(self):
  236. return self._offset
  237. @offset.setter
  238. def offset(self, offset):
  239. if not isinstance(offset, tuple):
  240. raise TypeError("Expected a tuple")
  241. if len(offset) != 2:
  242. raise ValueError("Expected tuple of length two.")
  243. if not isinstance(offset[0], (int, float)) or not isinstance(offset[1], (int, float)):
  244. raise TypeError("Expected number values.")
  245. self._offset = (float(offset[0]), float(offset[1]))
  246. @property
  247. def offset_x(self):
  248. return self._offset[0]
  249. @offset_x.setter
  250. def offset_x(self, x):
  251. if not isinstance(x, (int, float)):
  252. raise TypeError("Expected number value.")
  253. self._offset = (x, self._offset[1])
  254. @property
  255. def offset_y(self):
  256. return self._offset[1]
  257. @offset_y.setter
  258. def offset_y(self, y):
  259. if not isinstance(y, (int, float)):
  260. raise TypeError("Expected number value.")
  261. self._offset = (self._offset[0], y)
  262. @property
  263. def scale(self):
  264. return self._scale
  265. @scale.setter
  266. def scale(self, scale):
  267. if self._keepAspectRatio:
  268. if not isinstance(scale, (int, float)):
  269. raise TypeError("Expected number value.")
  270. self._scale = (scale, self._scale[1])
  271. else:
  272. if not isinstance(scale, tuple):
  273. raise TypeError("Expected a tuple")
  274. if len(scale) != 2:
  275. raise ValueError("Expected tuple of length two.")
  276. if not isinstance(scale[0], (int, float)) or not isinstance(scale[1], (int, float)):
  277. raise TypeError("Expected number values.")
  278. self._scale = scale
  279. self._updateTransformSurface()
  280. @property
  281. def keep_aspect_ratio(self):
  282. return self._keepAspectRatio
  283. @keep_aspect_ratio.setter
  284. def keep_aspect_ratio(self, keep):
  285. self._keepAspectRatio = (keep == True)
  286. self._updateTransformSurface()
  287. @property
  288. def align_center(self):
  289. return self._alignCenter
  290. @align_center.setter
  291. def align_center(self, center):
  292. self._alignCenter = (center == True)
  293. @property
  294. def scale_to_display(self):
  295. return self._scaleToDisplay
  296. @scale_to_display.setter
  297. def scale_to_display(self, todisplay):
  298. if todisplay == True:
  299. self._scaleToDisplay = True
  300. Events.listen("VIDEORESIZE", self._OnVideoResize)
  301. else:
  302. self._scaleToDisplay = False
  303. Events.unlisten("VIDEORESIZE", self._OnVideoResize)
  304. self._updateTransformSurface()
  305. def scale_to(self, target_resolution):
  306. if self._surface is not None:
  307. size = self._surface.get_size()
  308. nscale = (float(size[0]) / float(target_resolution[0]), float(size[1]) / float(target_resolution[1]))
  309. self.scale = nscale
  310. def set_surface(self, resolution=None):
  311. dsurf = Display.surface
  312. if resolution is None:
  313. if dsurf is not None:
  314. self._surface = dsurf.convert_alpha()
  315. self._surface.fill(pygame.Color(0,0,0,0))
  316. self._updateTransformSurface()
  317. else:
  318. if not isinstance(resolution, tuple):
  319. raise TypeError("Expected a tuple.")
  320. if len(resolution) != 2:
  321. raise ValueError("Expected a tuple of length two.")
  322. if not isinstance(resolution[0], int) or not isinstance(resolution[1], int):
  323. raise TypeError("Tuple expected to contain integers.")
  324. if dsurf is not None:
  325. self._surface = pygame.Surface(resolution, pygame.SRCALPHA, dsurf)
  326. else:
  327. self._surface = pygame.Surface(resolution, pygame.SRCALPHA)
  328. self._surface.fill(pygame.Color(0,0,0,0))
  329. self._updateTransformSurface()
  330. def _render(self, surface):
  331. if self._surface is None:
  332. self.set_surface()
  333. if self._surface is not None:
  334. if self._scaleDirty:
  335. self._updateTransformSurface()
  336. Node2D._render(self, self._surface)
  337. else:
  338. Node2D._render(self, surface)
  339. self._scale_and_blit(surface)
  340. def _scale_and_blit(self, dest):
  341. dsize = dest.get_size()
  342. src = self._surface
  343. if self._tsurface is not None:
  344. pygame.transform.scale(self._surface, self._tsurface.get_size(), self._tsurface)
  345. src = self._tsurface
  346. ssize = src.get_size()
  347. posx = self._offset[0]
  348. posy = self._offset[1]
  349. if self._alignCenter:
  350. if dsize[0] > ssize[0]:
  351. posx += (dsize[0] - ssize[0]) * 0.5
  352. if dsize[1] > ssize[1]:
  353. posy += (dsize[1] - ssize[1]) * 0.5
  354. pos = (int(posx), int(posy))
  355. dest.blit(src, pos)
  356. def _OnVideoResize(self, event, data):
  357. if self._scaleToDisplay:
  358. self._scaleDirty = True
  359. class NodeSprite(Node2D):
  360. def __init__(self, name="NodeSprite", parent=None):
  361. try:
  362. Node2D.__init__(self, name, parent)
  363. except NodeError as e:
  364. raise e
  365. self._NODESPRITE_DATA={
  366. "rect":[0,0,0,0],
  367. "image":"",
  368. "scale":[1.0, 1.0],
  369. "surface":None
  370. }
  371. @property
  372. def rect(self):
  373. return (self._NODESPRITE_DATA["rect"][0],
  374. self._NODESPRITE_DATA["rect"][1],
  375. self._NODESPRITE_DATA["rect"][2],
  376. self._NODESPRITE_DATA["rect"][3])
  377. @rect.setter
  378. def rect(self, rect):
  379. if not isinstance(rect, (list, tuple)):
  380. raise TypeError("Expected a list or tuple.")
  381. if len(rect) != 4:
  382. raise ValueError("rect value contains wrong number of values.")
  383. try:
  384. self.rect_x = rect[0]
  385. self.rect_y = rect[1]
  386. self.rect_width = rect[2]
  387. self.rect_height = rect[3]
  388. except Exception as e:
  389. raise e
  390. @property
  391. def rect_x(self):
  392. return self._NODESPRITE_DATA["rect"][0]
  393. @rect_x.setter
  394. def rect_x(self, v):
  395. if not isinstance(v, int):
  396. raise TypeError("Expected integer value.")
  397. self._NODESPRITE_DATA["rect"][0] = v
  398. @property
  399. def rect_y(self):
  400. return self._NODESPRITE_DATA["rect"][1]
  401. @rect_y.setter
  402. def rect_y(self, v):
  403. if not isinstance(v, int):
  404. raise TypeError("Expected integer value.")
  405. self._NODESPRITE_DATA["rect"][1] = v
  406. @property
  407. def rect_width(self):
  408. return self._NODESPRITE_DATA["rect"][2]
  409. @rect_width.setter
  410. def rect_width(self, v):
  411. if not isinstance(v, int):
  412. raise TypeError("Expected integer value.")
  413. self._NODESPRITE_DATA["rect"][2] = v
  414. @property
  415. def rect_height(self):
  416. return self._NODESPRITE_DATA["rect"][3]
  417. @rect_height.setter
  418. def rect_height(self, v):
  419. if not isinstance(v, int):
  420. raise TypeError("Expected integer value.")
  421. self._NODESPRITE_DATA["rect"][3] = v
  422. @property
  423. def center(self):
  424. r = self._NODESPRITE_DATA["rect"]
  425. return (int(r[0] + (r[2] * 0.5)), int(r[1] + (r[3] * 0.5)))
  426. @property
  427. def scale(self):
  428. return (self._NODESPRITE_DATA["scale"][0], self._NODESPRITE_DATA["scale"][1])
  429. @scale.setter(self, scale):
  430. if not isinstance(scale, (list, tuple)):
  431. raise TypeError("Expected a list or tuple.")
  432. if len(scale) != 2:
  433. raise ValueError("Scale contains wrong number of values.")
  434. try:
  435. self.scale_x = scale[0]
  436. self.scale_y = scale[1]
  437. except Exception as e:
  438. raise e
  439. @property
  440. def scale_x(self):
  441. return self._NODESPRITE_DATA["scale"][0]
  442. @scale_x.setter
  443. def scale_x(self, v):
  444. if not isinstance(v, (int, float)):
  445. raise TypeError("Expected number value.")
  446. self._NODESPRITE_DATA["scale"][0] = float(v)
  447. @property
  448. def scale_y(self):
  449. return self._NODESPRITE_DATA["scale"][1]
  450. @scale_y.setter
  451. def scale_y(self, v):
  452. if not isinstance(v, (int, float)):
  453. raise TypeError("Expected number value.")
  454. self._NODESPRITE_DATA["scale"][1] = float(v)
  455. @property
  456. def image(self):
  457. return self._NODESPRITE_DATA["image"]
  458. @image.setter
  459. def image(self, image):
  460. if self._NODESPRITE_DATA["image"] != "":
  461. self._NODESPRITE_DATA["surface"] = None # Clear reference to original surface.
  462. pass