gui.py 24KB

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