game.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. ########################################################################
  2. # Hello Worlds - Libre 3D RPG game.
  3. # Copyright (C) 2020 CYBERDEViL
  4. #
  5. # This file is part of Hello Worlds.
  6. #
  7. # Hello Worlds is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # Hello Worlds is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. #
  20. ########################################################################
  21. from LUIFrame import LUIFrame
  22. from LUILabel import LUILabel
  23. from LUIProgressbar import LUIProgressbar
  24. from LUILayouts import LUIHorizontalLayout
  25. from LUIVerticalLayout import LUIVerticalLayout
  26. from LUISprite import LUISprite
  27. from LUIObject import LUIObject
  28. from LUIInitialState import LUIInitialState
  29. from core.models import ItemModel
  30. class UI_ItemInfo(LUIFrame):
  31. def __init__(self, **kwargs):
  32. LUIFrame.__init__(self, style=LUIFrame.FS_raised, **kwargs)
  33. self.margin = 0
  34. self._model = None
  35. self._descLabelFmt = "Description: {}"
  36. self._descLabel = LUILabel("", parent=self, margin=0, left=3, top=3)
  37. self.hide()
  38. def setModel(self, model):
  39. self._model = model
  40. if model:
  41. self._descLabel.set_text(self._descLabelFmt.format(model.description))
  42. else: self._modelRemoved()
  43. def close(self): self.parent = None
  44. def _modelRemoved(self):
  45. self._descLabel.set_text(self._descLabelFmt)
  46. class UI_DragItem(LUIObject):
  47. icons = {
  48. 'unknown' : "assets/icons/unknown.png",
  49. 'empty' : "assets/icons/overlay_0.png"
  50. }
  51. def __init__(self, *args, **kwargs):
  52. LUIObject.__init__(self, *args)
  53. self.width = 48
  54. self.height = 48
  55. self._icon = LUISprite(self, self.icons['empty'], width=self.width, height=self.height)
  56. self.set_pos(100,100)
  57. class UI_Item(LUIObject):
  58. icons = {
  59. 'unknown' : "assets/icons/unknown.png",
  60. 'empty' : "assets/icons/overlay_0.png"
  61. }
  62. def __init__(self, *args, **kwargs):
  63. # Without solid=True it doesn't receive mouse-events
  64. LUIObject.__init__(self, *args, solid=True)
  65. self.width = 48
  66. self.height = 48
  67. self._model = None
  68. self._isDragging = False
  69. self._backupParent = self.parent
  70. self._dragItem = None
  71. self._icon = LUISprite(self, self.icons['empty'], width=self.width, height=self.height)
  72. self._label = LUILabel("", parent=self, margin=0, left=0, bottom=0)
  73. self._overlay = None
  74. LUIInitialState.init(self, kwargs)
  75. def mousePos(self):
  76. if base.mouseWatcherNode.hasMouse():
  77. mpos = base.mouseWatcherNode.getMouse()
  78. mousePos = (mpos.getX() * base.getAspectRatio(), mpos.getY())
  79. screenWidth = base.win.getProperties().getXSize()
  80. screenHeight = base.win.getProperties().getYSize()
  81. ratio = base.getAspectRatio()
  82. # https://stackoverflow.com/questions/929103/convert-a-number-range-to-another-range-maintaining-ratio
  83. left = (((mousePos[0] - -ratio) * (screenWidth - 0)) / (ratio - -ratio)) + 0
  84. top = (((mousePos[1] - 1) * (screenHeight - 0)) / (-1 - 1)) + 0
  85. return (left, top)
  86. return(0,0)
  87. def _dragWatcher(self, event=None):
  88. if not self._dragItem:
  89. print("Start drag")
  90. self._dragItem = UI_DragItem(base.luiRegion.root)
  91. self.unbind('mousemove')
  92. taskMgr.add(self._dragTask, 'dragWatcher')
  93. def _dragTask(self, task):
  94. self._dragItem.set_pos(self.mousePos())
  95. return task.cont
  96. def _stopDrag(self):
  97. if self._dragItem:
  98. print("Stop drag")
  99. taskMgr.remove('dragWatcher')
  100. self._dragItem.parent = None
  101. self._dragItem = None
  102. def on_mouseover(self, event):
  103. if self._model:
  104. pos = self.get_abs_pos()
  105. pos -= (-50,50)
  106. base.uiManager.spellInfoFrame.setModel(self._model)
  107. base.uiManager.spellInfoFrame.set_pos(pos)
  108. base.uiManager.spellInfoFrame.show()
  109. def on_mouseout(self, event):
  110. if self._model:
  111. base.uiManager.spellInfoFrame.hide()
  112. def on_mousedown(self, event):
  113. if self._model:
  114. #self.bind('mousemove', self._dragWatcher) # uncomment to enable drag 'n drop (that isn't complete yet)
  115. self._addOverlay()
  116. def on_mouseup(self, event):
  117. if self._model:
  118. self._stopDrag()
  119. self._removeOverlay()
  120. def _addOverlay(self):
  121. overlayPath = "assets/icons/overlay_1.png"
  122. if not self._overlay:
  123. self._overlay = LUISprite(self, overlayPath, width=self.width, height=self.height)
  124. def _removeOverlay(self):
  125. if self._overlay:
  126. self._overlay.parent = None
  127. self._overlay = None
  128. def setModel(self, model):
  129. self._model = model
  130. if model:
  131. self._icon.set_texture(model.iconPath, resize=False)
  132. self._label.set_text(model.name)
  133. else: self._modelRemoved()
  134. def _modelRemoved(self):
  135. self._icon.set_texture(self.icons['empty'], resize=False)
  136. self._label.set_text("")
  137. class UI_SpellBar(LUIFrame):
  138. def __init__(self, **kwargs):
  139. LUIFrame.__init__(self, style=LUIFrame.FS_sunken, **kwargs)
  140. self.height = 68
  141. self.margin = 0
  142. screenHeight = base.win.getProperties().getYSize()
  143. self.top = screenHeight - 68 - 3
  144. self.center_horizontal = True
  145. self._layout = LUIHorizontalLayout(parent=self)
  146. self._layout.spacing = 3
  147. maxItems = 10
  148. self._items = []
  149. for i in range(0, maxItems):
  150. self._items.append(UI_Item(self._layout.cell()))
  151. """ TODO TMP add some spells for testing
  152. These spells or actions? shouldn't be static like this..
  153. """
  154. spell1Test = ItemModel(
  155. name="Melee 1",
  156. iconPath="assets/icons/attack_0.png",
  157. desc="Melee attack level 1")
  158. self._items[0].setModel(spell1Test)
  159. spell1Test = ItemModel(
  160. name="Melee 2",
  161. iconPath="assets/icons/attack_0.png",
  162. desc="Melee attack level 2")
  163. self._items[1].setModel(spell1Test)
  164. base.accept('WINDOW_RESIZED', self.reposition)
  165. def reposition(self):
  166. self.top = base.win.getProperties().getYSize() - self.height - 10
  167. def close(self): self.parent = None
  168. class UI_PlayerInfoBox(LUIFrame):
  169. def __init__(self, parent=None, top=10, left=10):
  170. if not parent: parent = base.luiRegion.root
  171. LUIFrame.__init__(self, parent=parent, style=LUIFrame.FS_sunken, width=170, height=80, margin=0, top=top, left=left)
  172. self._layout = LUIVerticalLayout(self)
  173. self.nameLabel = LUILabel(parent=self._layout.cell(), text="Name: ")
  174. self.healthBar = LUIProgressbar(parent=self._layout.cell(), width=145, label_fmt="Health: {value}/{max}")
  175. self.energyBar = LUIProgressbar(parent=self._layout.cell(), width=145, label_fmt="Energy: {value}/{max}")
  176. self.healthBar.set_max(base.world.player.character.stats.health.max)
  177. self.healthBar.set_value(base.world.player.character.stats.health.value)
  178. self.energyBar.set_max(base.world.player.character.stats.energy.max)
  179. self.energyBar.set_value(base.world.player.character.stats.energy.value)
  180. base.world.player.character.stats.health.valueChanged.connect(self._healthChanged)
  181. base.world.player.character.stats.energy.valueChanged.connect(self._energyChanged)
  182. def _healthChanged(self, value): self.healthBar.set_value(value)
  183. def _energyChanged(self, value): self.energyBar.set_value(value)
  184. def close(self): self.parent = None
  185. class UI_CreatureInfoBox:
  186. def __init__(self, parent=None, top=100, left=10):
  187. if not parent: parent = base.luiRegion.root
  188. self._currentSpawnId = -1
  189. self._container = LUIFrame(parent=parent, width=175, height=55, style=LUIFrame.FS_sunken, margin=0, top=top, left=left)
  190. self.nameLabel = LUILabel(parent=self._container, text="Name: ", top=0)
  191. self.healthBar = LUIProgressbar(parent=self._container, width=145, top=20, label_fmt="Health: {value}/{max}")
  192. base.world.npcsManager.selectedNpcModel.changed.connect(self._selectionChanged)
  193. self.hide()
  194. def hasMouse(self): return self._container.hasMouse()
  195. def _selectionChanged(self, spawnId):
  196. if self._currentSpawnId == spawnId and self._currentSpawnId != -1:
  197. # Clicked twice or more
  198. # This is a TMP-test.. remove later TODO
  199. npc = base.world.npcsManager.getSpawn(self._currentSpawnId) # creaturesManager.Creature
  200. npc.stats.health.value -= 10
  201. if npc.stats.health.value <= 0: # NPC died
  202. spawnId = -1
  203. # Disconnect previously set connections
  204. if self._currentSpawnId != -1 and self._currentSpawnId != spawnId:
  205. oldNpc = base.world.npcsManager.getSpawn(self._currentSpawnId) # creaturesManager.Creature
  206. oldNpc.stats.health.valueChanged.disconnect(self._healthChanged)
  207. oldNpc.removeSelectPlane()
  208. # Hide if selected id is -1
  209. if spawnId == -1:
  210. self.hide()
  211. self._currentSpawnId = -1
  212. return
  213. if self._currentSpawnId != spawnId:
  214. self.show()
  215. npc = base.world.npcsManager.getSpawn(spawnId) # creaturesManager.Creature
  216. npc.addSelectPlane()
  217. # Name
  218. self.nameLabel.text = "{0} (id: {1} lvl: {2})".format(npc.data.name, npc.id, npc.stats.level.value)
  219. # Health
  220. self.healthBar.set_max(npc.stats.health.max)
  221. self.healthBar.set_value(npc.stats.health.value)
  222. self._currentSpawnId = spawnId
  223. npc.stats.health.valueChanged.connect(self._healthChanged)
  224. def _healthChanged(self, value):
  225. npc = base.world.npcsManager.getSpawn(self._currentSpawnId)
  226. self.healthBar.set_value(npc.stats.health.value)
  227. def close(self):
  228. self._container.parent = None
  229. self._container = None
  230. def hide(self):
  231. self._container.hide()
  232. def show(self):
  233. self._container.show()
  234. def isVisible(self): return self._container.visible