gui.py 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. # coding: utf-8
  2. import typing
  3. import weakref
  4. from math import floor
  5. import pyglet
  6. from pyglet.window import mouse
  7. import cocos
  8. from cocos import collision_model
  9. from cocos import euclid
  10. from cocos.director import director
  11. from cocos.layer import Layer
  12. from cocos.layer import ScrollableLayer
  13. from cocos.sprite import Sprite
  14. from synergine2.config import Config
  15. from synergine2.log import SynergineLogger
  16. from synergine2.terminals import Terminal
  17. from synergine2.terminals import TerminalPackage
  18. from synergine2_cocos2d.actor import Actor
  19. from synergine2_cocos2d.exception import OuterWorldPosition
  20. from synergine2_cocos2d.layer import LayerManager
  21. from synergine2_cocos2d.middleware import TMXMiddleware
  22. # class GridManager(object):
  23. # def __init__(
  24. # self,
  25. # layer: Layer,
  26. # square_width: int,
  27. # border: int=0,
  28. # ):
  29. # self.layer = layer
  30. # self.square_width = square_width
  31. # self.border = border
  32. #
  33. # @property
  34. # def final_width(self):
  35. # return self.square_width + self.border
  36. #
  37. # def scale_sprite(self, sprite: Sprite):
  38. # sprite.scale_x = self.final_width / sprite.image.width
  39. # sprite.scale_y = self.final_width / sprite.image.height
  40. #
  41. # def position_sprite(self, sprite: Sprite, grid_position):
  42. # grid_x = grid_position[0]
  43. # grid_y = grid_position[1]
  44. # sprite.position = grid_x * self.final_width, grid_y * self.final_width
  45. #
  46. # def get_window_position(self, grid_position_x, grid_position_y):
  47. # grid_x = grid_position_x
  48. # grid_y = grid_position_y
  49. # return grid_x * self.final_width, grid_y * self.final_width
  50. #
  51. # def get_grid_position(self, window_x, window_y, z=0) -> tuple:
  52. # window_size = director.get_window_size()
  53. #
  54. # window_center_x = window_size[0] // 2
  55. # window_center_y = window_size[1] // 2
  56. #
  57. # window_relative_x = window_x - window_center_x
  58. # window_relative_y = window_y - window_center_y
  59. #
  60. # real_width = self.final_width * self.layer.scale
  61. #
  62. # return int(window_relative_x // real_width),\
  63. # int(window_relative_y // real_width),\
  64. # z
  65. #
  66. #
  67. # class GridLayerMixin(object):
  68. # def __init__(self, *args, **kwargs):
  69. # square_width = kwargs.pop('square_width', 32)
  70. # square_border = kwargs.pop('square_border', 2)
  71. # self.grid_manager = GridManager(
  72. # self,
  73. # square_width=square_width,
  74. # border=square_border,
  75. # )
  76. # super().__init__(*args, **kwargs)
  77. class GridManager(object):
  78. def __init__(
  79. self,
  80. cell_width: int,
  81. cell_height: int,
  82. world_width: int,
  83. world_height: int,
  84. ) -> None:
  85. self.cell_width = cell_width
  86. self.cell_height = cell_height
  87. self.world_width = world_width
  88. self.world_height = world_height
  89. def get_grid_position(self, pixel_position: typing.Tuple[int, int]) -> typing.Tuple[int, int]:
  90. pixel_x, pixel_y = pixel_position
  91. cell_x = int(floor(pixel_x / self.cell_width))
  92. cell_y = int(floor(pixel_y / self.cell_height))
  93. if cell_x > self.world_width or cell_y > self.world_height or cell_x < 0 or cell_y < 0:
  94. raise OuterWorldPosition('Position "{}" is outer world ({}x{})'.format(
  95. (cell_x, cell_y),
  96. self.world_width,
  97. self.world_height,
  98. ))
  99. return cell_x, cell_y
  100. def get_pixel_position_of_grid_position(self, grid_position: typing.Tuple[int, int]) -> typing.Tuple[int, int]:
  101. return grid_position[0] * self.cell_width, grid_position[1] * self.cell_height
  102. class MinMaxRect(cocos.cocosnode.CocosNode):
  103. def __init__(self, layer_manager: LayerManager):
  104. super(MinMaxRect, self).__init__()
  105. self.layer_manager = layer_manager
  106. self.color3 = (20, 20, 20)
  107. self.vertexes = [(0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]
  108. self.visible = False
  109. def adjust_from_w_minmax(self, wminx, wmaxx, wminy, wmaxy):
  110. # asumes world to screen preserves order
  111. sminx, sminy = self.layer_manager.scrolling_manager.world_to_screen(wminx, wminy)
  112. smaxx, smaxy = self.layer_manager.scrolling_manager.world_to_screen(wmaxx, wmaxy)
  113. self.vertexes = [(sminx, sminy), (sminx, smaxy), (smaxx, smaxy), (smaxx, sminy)]
  114. def draw(self):
  115. if not self.visible:
  116. return
  117. pyglet.gl.glLineWidth(1) # deprecated
  118. pyglet.gl.glColor3ub(*self.color3)
  119. pyglet.gl.glBegin(pyglet.gl.GL_LINE_STRIP)
  120. for v in self.vertexes:
  121. pyglet.gl.glVertex2f(*v)
  122. pyglet.gl.glVertex2f(*self.vertexes[0])
  123. pyglet.gl.glEnd()
  124. # rectangle
  125. pyglet.gl.glColor4f(0, 0, 0, 0.5)
  126. pyglet.gl.glBegin(pyglet.gl.GL_QUADS)
  127. pyglet.gl.glVertex3f(self.vertexes[0][0], self.vertexes[0][1], 0)
  128. pyglet.gl.glVertex3f(self.vertexes[1][0], self.vertexes[1][1], 0)
  129. pyglet.gl.glVertex3f(self.vertexes[2][0], self.vertexes[2][1], 0)
  130. pyglet.gl.glVertex3f(self.vertexes[3][0], self.vertexes[3][1], 0)
  131. pyglet.gl.glEnd()
  132. def set_vertexes_from_minmax(self, minx, maxx, miny, maxy):
  133. self.vertexes = [(minx, miny), (minx, maxy), (maxx, maxy), (maxx, miny)]
  134. class EditLayer(cocos.layer.Layer):
  135. is_event_handler = True
  136. def __init__(
  137. self,
  138. config: Config,
  139. logger: SynergineLogger,
  140. layer_manager: LayerManager,
  141. grid_manager: GridManager,
  142. worldview,
  143. bindings=None,
  144. fastness=None,
  145. autoscroll_border=10,
  146. autoscroll_fastness=None,
  147. wheel_multiplier=None,
  148. zoom_min=None,
  149. zoom_max=None,
  150. zoom_fastness=None,
  151. mod_modify_selection=None,
  152. mod_restricted_mov=None,
  153. ):
  154. # TODO: Clean init params
  155. super(EditLayer, self).__init__()
  156. self.config = config
  157. self.logger = logger
  158. self.layer_manager = layer_manager
  159. self.grid_manager = grid_manager
  160. self.bindings = bindings
  161. buttons = {}
  162. modifiers = {}
  163. for k in bindings:
  164. buttons[bindings[k]] = 0
  165. modifiers[bindings[k]] = 0
  166. self.buttons = buttons
  167. self.modifiers = modifiers
  168. self.fastness = fastness
  169. self.autoscroll_border = autoscroll_border
  170. self.autoscroll_fastness = autoscroll_fastness
  171. self.wheel_multiplier = wheel_multiplier
  172. self.zoom_min = zoom_min
  173. self.zoom_max = zoom_max
  174. self.zoom_fastness = zoom_fastness
  175. self.mod_modify_selection = mod_modify_selection
  176. self.mod_restricted_mov = mod_restricted_mov
  177. self.weak_scroller = weakref.ref(self.layer_manager.scrolling_manager)
  178. self.weak_worldview = weakref.ref(worldview)
  179. self.wwidth = worldview.width
  180. self.wheight = worldview.height
  181. self.autoscrolling = False
  182. self.drag_selecting = False
  183. self.drag_moving = False
  184. self.restricted_mov = False
  185. self.wheel = 0
  186. self.dragging = False
  187. self.keyscrolling = False
  188. self.keyscrolling_descriptor = (0, 0)
  189. self.wdrag_start_point = (0, 0)
  190. self.elastic_box = None # type: MinMaxRect
  191. self.elastic_box_wminmax = 0, 0, 0, 0
  192. self.selection = {}
  193. self.screen_mouse = (0, 0)
  194. self.world_mouse = (0, 0)
  195. self.sleft = None
  196. self.sright = None
  197. self.sbottom = None
  198. self.s_top = None
  199. # opers that change cshape must ensure it goes to False,
  200. # selection opers must ensure it goes to True
  201. self.selection_in_collman = True
  202. # TODO: Hardcoded here, should be obtained from level properties or calc
  203. # from available actors or current actors in worldview
  204. gsize = 32 * 1.25
  205. self.collision_manager = collision_model.CollisionManagerGrid(
  206. -gsize,
  207. self.wwidth + gsize,
  208. -gsize,
  209. self.wheight + gsize,
  210. gsize,
  211. gsize,
  212. )
  213. self.schedule(self.update)
  214. self.selectable_actors = []
  215. def set_selectable(self, actor: Actor) -> None:
  216. self.selectable_actors.append(actor)
  217. self.collision_manager.add(actor)
  218. def unset_selectable(self, actor: Actor) -> None:
  219. self.selectable_actors.remove(actor)
  220. self.collision_manager.remove_tricky(actor)
  221. def draw(self, *args, **kwargs) -> None:
  222. for actor in self.selectable_actors:
  223. if actor.need_update_cshape:
  224. if self.collision_manager.knows(actor):
  225. self.collision_manager.remove_tricky(actor)
  226. actor.update_cshape()
  227. self.collision_manager.add(actor)
  228. def on_enter(self):
  229. super(EditLayer, self).on_enter()
  230. scene = self.get_ancestor(cocos.scene.Scene)
  231. if self.elastic_box is None:
  232. self.elastic_box = MinMaxRect(self.layer_manager)
  233. scene.add(self.elastic_box, z=10)
  234. def update(self, dt):
  235. mx = self.buttons['right'] - self.buttons['left']
  236. my = self.buttons['up'] - self.buttons['down']
  237. dz = self.buttons['zoomin'] - self.buttons['zoomout']
  238. # scroll
  239. if self.autoscrolling:
  240. self.update_autoscroll(dt)
  241. else:
  242. # care for keyscrolling
  243. new_keyscrolling = ((len(self.selection) == 0) and
  244. (mx != 0 or my != 0))
  245. new_keyscrolling_descriptor = (mx, my)
  246. if ((new_keyscrolling != self.keyscrolling) or
  247. (new_keyscrolling_descriptor != self.keyscrolling_descriptor)):
  248. self.keyscrolling = new_keyscrolling
  249. self.keyscrolling_descriptor = new_keyscrolling_descriptor
  250. fastness = 1.0
  251. if mx != 0 and my != 0:
  252. fastness *= 0.707106 # 1/sqrt(2)
  253. self.autoscrolling_sdelta = (0.5 * fastness * mx, 0.5 * fastness * my)
  254. if self.keyscrolling:
  255. self.update_autoscroll(dt)
  256. # selection move
  257. if self.drag_moving:
  258. # update positions
  259. wx, wy = self.world_mouse
  260. dx = wx - self.wdrag_start_point[0]
  261. dy = wy - self.wdrag_start_point[1]
  262. if self.restricted_mov:
  263. if abs(dy) > abs(dx):
  264. dx = 0
  265. else:
  266. dy = 0
  267. dpos = euclid.Vector2(dx, dy)
  268. for actor in self.selection:
  269. old_pos = self.selection[actor].center
  270. new_pos = old_pos + dpos
  271. grid_pos = self.grid_manager.get_grid_position(new_pos)
  272. grid_pixel_pos = self.grid_manager.get_pixel_position_of_grid_position(grid_pos)
  273. # TODO: clamp new_pos so actor into world boundaries ?
  274. actor.update_position(grid_pixel_pos)
  275. scroller = self.weak_scroller()
  276. # zoom
  277. zoom_change = (dz != 0 or self.wheel != 0)
  278. if zoom_change:
  279. if self.mouse_into_world():
  280. wzoom_center = self.world_mouse
  281. szoom_center = self.screen_mouse
  282. else:
  283. # decay to scroller unadorned
  284. wzoom_center = None
  285. if self.wheel != 0:
  286. dt_dz = 0.01666666 * self.wheel
  287. self.wheel = 0
  288. else:
  289. dt_dz = dt * dz
  290. zoom = scroller.scale + dt_dz * self.zoom_fastness
  291. if zoom < self.zoom_min:
  292. zoom = self.zoom_min
  293. elif zoom > self.zoom_max:
  294. zoom = self.zoom_max
  295. scroller.scale = zoom
  296. if wzoom_center is not None:
  297. # postprocess toward 'world point under mouse the same before
  298. # and after zoom' ; other restrictions may prevent fully comply
  299. wx1, wy1 = self.layer_manager.scrolling_manager.screen_to_world(*szoom_center)
  300. fx = scroller.restricted_fx + (wzoom_center[0] - wx1)
  301. fy = scroller.restricted_fy + (wzoom_center[1] - wy1)
  302. scroller.set_focus(fx, fy)
  303. def update_mouse_position(self, sx, sy):
  304. self.screen_mouse = sx, sy
  305. self.world_mouse = self.layer_manager.scrolling_manager.screen_to_world(sx, sy)
  306. # handle autoscroll
  307. border = self.autoscroll_border
  308. if border is not None:
  309. # sleft and companions includes the border
  310. scroller = self.weak_scroller()
  311. self.update_view_bounds()
  312. sdx = 0.0
  313. if sx < self.sleft:
  314. sdx = sx - self.sleft
  315. elif sx > self.sright:
  316. sdx = sx - self.sright
  317. sdy = 0.0
  318. if sy < self.sbottom:
  319. sdy = sy - self.sbottom
  320. elif sy > self.s_top:
  321. sdy = sy - self.s_top
  322. self.autoscrolling = sdx != 0.0 or sdy != 0.0
  323. if self.autoscrolling:
  324. self.autoscrolling_sdelta = (sdx / border, sdy / border)
  325. def update_autoscroll(self, dt):
  326. fraction_sdx, fraction_sdy = self.autoscrolling_sdelta
  327. scroller = self.weak_scroller()
  328. worldview = self.weak_worldview()
  329. f = self.autoscroll_fastness
  330. wdx = (fraction_sdx * f * dt) / scroller.scale / worldview.scale
  331. wdy = (fraction_sdy * f * dt) / scroller.scale / worldview.scale
  332. # ask scroller to try scroll (wdx, wdy)
  333. fx = scroller.restricted_fx + wdx
  334. fy = scroller.restricted_fy + wdy
  335. scroller.set_focus(fx, fy)
  336. self.world_mouse = self.layer_manager.scrolling_manager.screen_to_world(*self.screen_mouse)
  337. self.adjust_elastic_box()
  338. # self.update_view_bounds()
  339. def update_view_bounds(self):
  340. scroller = self.weak_scroller()
  341. scx, scy = self.layer_manager.scrolling_manager.world_to_screen(
  342. scroller.restricted_fx,
  343. scroller.restricted_fy,
  344. )
  345. hw = scroller.view_w / 2.0
  346. hh = scroller.view_h / 2.0
  347. border = self.autoscroll_border
  348. self.sleft = scx - hw + border
  349. self.sright = scx + hw - border
  350. self.sbottom = scy - hh + border
  351. self.s_top = scy + hh - border
  352. def mouse_into_world(self):
  353. worldview = self.weak_worldview()
  354. # TODO: allow lower limits != 0 ?
  355. return ((0 <= self.world_mouse[0] <= worldview.width) and
  356. (0 <= self.world_mouse[1] <= worldview.height))
  357. def on_key_press(self, k, m):
  358. binds = self.bindings
  359. if k in binds:
  360. self.buttons[binds[k]] = 1
  361. self.modifiers[binds[k]] = 1
  362. return True
  363. return False
  364. def on_key_release(self, k, m):
  365. binds = self.bindings
  366. if k in binds:
  367. self.buttons[binds[k]] = 0
  368. self.modifiers[binds[k]] = 0
  369. return True
  370. return False
  371. def on_mouse_motion(self, sx, sy, dx, dy):
  372. self.update_mouse_position(sx, sy)
  373. def on_mouse_leave(self, sx, sy):
  374. self.autoscrolling = False
  375. def on_mouse_press(self, x, y, buttons, modifiers):
  376. if self.logger.is_debug:
  377. rx, ry = self.layer_manager.scrolling_manager.screen_to_world(x, y)
  378. self.logger.debug('GUI click: x: {}, y: {}, rx: {}, ry: {}'.format(x, y, rx, ry))
  379. def on_mouse_release(self, sx, sy, button, modifiers):
  380. # should we handle here mod_restricted_mov ?
  381. wx, wy = self.layer_manager.scrolling_manager.screen_to_world(sx, sy)
  382. modify_selection = modifiers & self.mod_modify_selection
  383. if self.dragging:
  384. # ignore all buttons except left button
  385. if button != mouse.LEFT:
  386. return
  387. if self.drag_selecting:
  388. self.end_drag_selection(wx, wy, modify_selection)
  389. elif self.drag_moving:
  390. self.end_drag_move(wx, wy)
  391. self.dragging = False
  392. else:
  393. if button == mouse.LEFT:
  394. self.end_click_selection(wx, wy, modify_selection)
  395. def end_click_selection(self, wx, wy, modify_selection):
  396. under_mouse_unique = self.single_actor_from_mouse()
  397. if modify_selection:
  398. # toggle selected status for unique
  399. if under_mouse_unique in self.selection:
  400. self.selection_remove(under_mouse_unique)
  401. elif under_mouse_unique is not None:
  402. self.selection_add(under_mouse_unique)
  403. else:
  404. # new_selected becomes the current selected
  405. self.selection.clear()
  406. if under_mouse_unique is not None:
  407. self.selection_add(under_mouse_unique)
  408. def selection_add(self, actor):
  409. self.selection[actor] = actor.cshape.copy()
  410. def selection_remove(self, actor):
  411. del self.selection[actor]
  412. def end_drag_selection(self, wx, wy, modify_selection):
  413. new_selection = self.collision_manager.objs_into_box(*self.elastic_box_wminmax)
  414. if not modify_selection:
  415. # new_selected becomes the current selected
  416. self.selection.clear()
  417. for actor in new_selection:
  418. self.selection_add(actor)
  419. self.elastic_box.visible = False
  420. self.drag_selecting = False
  421. def on_mouse_drag(self, sx, sy, dx, dy, buttons, modifiers):
  422. # TODO: inhibir esta llamada si estamos fuera de la client area / viewport
  423. self.update_mouse_position(sx, sy)
  424. if not buttons & mouse.LEFT:
  425. # ignore except for left-btn-drag
  426. return
  427. if not self.dragging:
  428. print("begin drag")
  429. self.begin_drag()
  430. return
  431. if self.drag_selecting:
  432. # update elastic box
  433. self.adjust_elastic_box()
  434. elif self.drag_moving:
  435. self.restricted_mov = (modifiers & self.mod_restricted_mov)
  436. def adjust_elastic_box(self):
  437. # when elastic_box visible this method needs to be called any time
  438. # world_mouse changes or screen_to_world results changes (scroll, etc)
  439. wx0, wy0 = self.wdrag_start_point
  440. wx1, wy1 = self.world_mouse
  441. wminx = min(wx0, wx1)
  442. wmaxx = max(wx0, wx1)
  443. wminy = min(wy0, wy1)
  444. wmaxy = max(wy0, wy1)
  445. self.elastic_box_wminmax = wminx, wmaxx, wminy, wmaxy
  446. self.elastic_box.adjust_from_w_minmax(*self.elastic_box_wminmax)
  447. def begin_drag(self):
  448. self.dragging = True
  449. self.wdrag_start_point = self.world_mouse
  450. under_mouse_unique = self.single_actor_from_mouse()
  451. if under_mouse_unique is None:
  452. # begin drag selection
  453. self.drag_selecting = True
  454. self.adjust_elastic_box()
  455. self.elastic_box.visible = True
  456. print("begin drag selection: drag_selecting, drag_moving",
  457. self.drag_selecting, self.drag_moving)
  458. else:
  459. # want drag move
  460. if under_mouse_unique in self.selection:
  461. # want to move current selection
  462. pass
  463. else:
  464. # change selection before moving
  465. self.selection.clear()
  466. self.selection_add(under_mouse_unique)
  467. self.begin_drag_move()
  468. def begin_drag_move(self):
  469. # begin drag move
  470. self.drag_moving = True
  471. # how-to update collman: remove/add vs clear/add all
  472. # when total number of actors is low anyone will be fine,
  473. # with high numbers, most probably we move only a small fraction
  474. # For simplicity I choose remove/add, albeit a hybrid aproach
  475. # can be implemented later
  476. self.set_selection_in_collman(False)
  477. # print "begin drag: drag_selecting, drag_moving", self.drag_selecting, self.drag_moving
  478. def end_drag_move(self, wx, wy):
  479. self.set_selection_in_collman(True)
  480. for actor in self.selection:
  481. self.selection[actor] = actor.cshape.copy()
  482. self.drag_moving = False
  483. def single_actor_from_mouse(self):
  484. under_mouse = self.collision_manager.objs_touching_point(*self.world_mouse)
  485. if len(under_mouse) == 0:
  486. return None
  487. # return the one with the center most near to mouse, if tie then
  488. # an arbitrary in the tie
  489. nearest = None
  490. near_d = None
  491. p = euclid.Vector2(*self.world_mouse)
  492. for actor in under_mouse:
  493. d = (actor.cshape.center - p).magnitude_squared()
  494. if nearest is None or (d < near_d):
  495. nearest = actor
  496. near_d = d
  497. return nearest
  498. def set_selection_in_collman(self, bool_value):
  499. if self.selection_in_collman == bool_value:
  500. return
  501. self.selection_in_collman = bool_value
  502. if bool_value:
  503. for actor in self.selection:
  504. self.collision_manager.add(actor)
  505. else:
  506. for actor in self.selection:
  507. self.collision_manager.remove_tricky(actor)
  508. def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
  509. # TODO: check if mouse over scroller viewport?
  510. self.wheel += scroll_y * self.wheel_multiplier
  511. class MainLayer(ScrollableLayer):
  512. is_event_handler = True
  513. def __init__(
  514. self,
  515. layer_manager: LayerManager,
  516. grid_manager: GridManager,
  517. width: int,
  518. height: int,
  519. scroll_step: int=100,
  520. ) -> None:
  521. super().__init__()
  522. self.layer_manager = layer_manager
  523. self.scroll_step = scroll_step
  524. self.grid_manager = grid_manager
  525. self.width = width
  526. self.height = height
  527. self.px_width = width
  528. self.px_height = height
  529. class Gui(object):
  530. def __init__(
  531. self,
  532. config: Config,
  533. logger: SynergineLogger,
  534. terminal: Terminal,
  535. read_queue_interval: float= 1/60.0,
  536. ):
  537. self.config = config
  538. self.logger = logger
  539. self._read_queue_interval = read_queue_interval
  540. self.terminal = terminal
  541. self.cycle_duration = self.config.core.cycle_duration
  542. cocos.director.director.init(
  543. width=640,
  544. height=480,
  545. vsync=True,
  546. resizable=True,
  547. )
  548. # Enable blending
  549. pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
  550. pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
  551. # Enable transparency
  552. pyglet.gl.glEnable(pyglet.gl.GL_ALPHA_TEST)
  553. pyglet.gl.glAlphaFunc(pyglet.gl.GL_GREATER, .1)
  554. def run(self):
  555. self.before_run()
  556. pyglet.clock.schedule_interval(
  557. lambda *_, **__: self.terminal.read(),
  558. self._read_queue_interval,
  559. )
  560. cocos.director.director.run(self.get_main_scene())
  561. def before_run(self) -> None:
  562. pass
  563. def get_main_scene(self) -> cocos.cocosnode.CocosNode:
  564. raise NotImplementedError()
  565. def before_received(self, package: TerminalPackage):
  566. pass
  567. def after_received(self, package: TerminalPackage):
  568. pass
  569. class TMXGui(Gui):
  570. def __init__(
  571. self,
  572. config: Config,
  573. logger: SynergineLogger,
  574. terminal: Terminal,
  575. read_queue_interval: float = 1 / 60.0,
  576. map_dir_path: str=None,
  577. ):
  578. assert map_dir_path
  579. super(TMXGui, self).__init__(
  580. config,
  581. logger,
  582. terminal,
  583. read_queue_interval,
  584. )
  585. self.map_dir_path = map_dir_path
  586. self.layer_manager = LayerManager(
  587. self.config,
  588. self.logger,
  589. middleware=TMXMiddleware(
  590. self.config,
  591. self.logger,
  592. self.map_dir_path,
  593. ),
  594. )
  595. self.layer_manager.init()
  596. self.layer_manager.center()
  597. def get_main_scene(self) -> cocos.cocosnode.CocosNode:
  598. return self.layer_manager.main_scene