models.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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 core.signals import Signal
  22. class Model:
  23. """ A model is used for data and data only. Other components can use
  24. a model to get data. A model has signals to which others can connect
  25. a callback; so they get notified when the data is updated or the
  26. model is destroyed.
  27. So this has nothing to do with a 3d model!
  28. """
  29. def __init__(self):
  30. ## Universal Signal emitted when this model is destroyed.
  31. self.destroyed = Signal()
  32. ## Universal Signal emitted when this model has been changed.
  33. self.changed = Signal()
  34. def destroy(self):
  35. """ This should always be called before deletion of this object,
  36. so that instances using this object know it is going to be
  37. deleted.
  38. """
  39. self.destroyed.emit()
  40. class ItemModel(Model):
  41. """ Basic item model
  42. Game items and spells will extend this.
  43. """
  44. def __init__(self, name="", iconPath="assets/icons/unknown.png", desc=""):
  45. """
  46. @param name: Item name
  47. @type name: str
  48. @param iconPath: Item path
  49. @type iconPath: str
  50. @param desc: Item description
  51. @type desc: str
  52. """
  53. Model.__init__(self)
  54. # TODO check if iconPath exists, else set assets/icons/unknown.png
  55. self._name = name
  56. self._iconPath = iconPath
  57. self._desc = desc
  58. @property
  59. def name(self): return self._name
  60. @property
  61. def iconPath(self): return self._iconPath
  62. @property
  63. def description(self): return self._desc
  64. @name.setter
  65. def name(self, value): self._name = value
  66. @iconPath.setter
  67. def iconPath(self, value): self._iconPath = value
  68. @description.setter
  69. def description(self, value): self._desc = value
  70. from core.db import Spells
  71. from core.spells import SpellStatus
  72. class SpellModel(Model):
  73. """ Spell model
  74. """
  75. def __init__(self, spellId):
  76. """
  77. @param spellId: Spell ID
  78. @type spellId: str
  79. """
  80. ## statusChange emits it's status
  81. self.statusChanged = Signal(int)
  82. Model.__init__(self)
  83. self._data = Spells[spellId]
  84. self._status = SpellStatus.none
  85. @property
  86. def data(self): return self._data
  87. @property
  88. def status(self): return self._status
  89. def isCooling(self): return bool(self._status == SpellStatus.cooling)
  90. def isCasting(self): return bool(self._status == SpellStatus.casting)
  91. def setStatus(self, status):
  92. """
  93. @param status: SpellStatus.*
  94. @type status: int
  95. """
  96. self._status = status
  97. self.statusChanged.emit(status)
  98. class SpellItemModel(ItemModel):
  99. """ Sort of proxy for the UI
  100. """
  101. def __init__(self, spellModel):
  102. """
  103. @param spellModel:
  104. @type spellModel: SpellModel
  105. """
  106. ## statusChange emits it's status
  107. self.statusChanged = Signal(int)
  108. ItemModel.__init__(self)
  109. self._model = spellModel
  110. self._model.statusChanged.connect(self._statusChanged)
  111. self._modelChanged()
  112. def _statusChanged(self, status):
  113. self.statusChanged.emit(status)
  114. def _modelChanged(self):
  115. self.name = self._model.data.name
  116. self.iconPath = self._model.data.iconPath()
  117. self.description = self._model.data.desc
  118. @property
  119. def data(self): return self._model.data
  120. @property
  121. def status(self): return self._model.status
  122. class SpellsModel(Model):
  123. """ Spells container for NPC's and PlayerCharacter
  124. """
  125. def __init__(self, spells=[]):
  126. """
  127. @param spells: List with spell-id's
  128. @type spells: list
  129. """
  130. Model.__init__(self)
  131. ## Protected list with SpellModel's
  132. self._spells = {}
  133. self.isCasting = False
  134. if spells: self.setSpellIds(spells)
  135. def __iter__(self):
  136. """ Iterating over this class will yield SpellModel's
  137. @rtype: SpellModel's
  138. @return:
  139. """
  140. for spell in self._spells.values(): yield spell
  141. def __getitem__(self, key):
  142. """ Get SpellModel for a spellId
  143. @param key: spellId
  144. @type key: str
  145. @rtype: SpellModel
  146. @return:
  147. """
  148. return self._spells[key]
  149. def __list__(self): return self._spells.values()
  150. def __len__(self): return len(self._spells)
  151. def addSpellId(self, spellId):
  152. """ Adds a spell by id
  153. @param spellId: Spell id
  154. @type spellId: str
  155. """
  156. self._spells.update({str(spellId) : SpellModel(spellId)})
  157. def setSpellIds(self, spellIds):
  158. """ Sets spells from list with spell-id's
  159. @param spellIds: List with spell-id's
  160. @type spellIds: list
  161. """
  162. for spellId in spellIds: self.addSpellId(spellId)
  163. class StatsModel(Model):
  164. def __init__(self, rawData={}):
  165. Model.__init__(self)
  166. self.setRawData(rawData)
  167. def setRawData(self, rawData):
  168. self._data = {
  169. 'level' : IntModel(
  170. max=rawData.level.max,
  171. value=rawData.level.value),
  172. 'health' : IntModel(
  173. max=rawData.health.max,
  174. value=rawData.health.value),
  175. 'energy' : IntModel(
  176. max=rawData.energy.max,
  177. value=rawData.energy.value),
  178. 'strength' : 1,
  179. 'stamina' : 1,
  180. 'resistance': 2,
  181. 'reflexes' : 0.5,
  182. 'spitit' : 0,
  183. }
  184. @property
  185. def health(self): return self._data['health']
  186. @property
  187. def level(self): return self._data['level']
  188. @property
  189. def energy(self): return self._data['energy']
  190. class PlayerStatsModel(StatsModel):
  191. def __init__(self, rawData={}):
  192. StatsModel.__init__(self, rawData=rawData)
  193. @property
  194. def experience(self): return self._data['experience']
  195. def setRawData(self, rawData):
  196. StatsModel.setRawData(self, rawData)
  197. self._data.update({
  198. 'experience' : IntModel(
  199. max=rawData.experience.max,
  200. value=rawData.experience.value)
  201. })
  202. class SelectedNpcModel(Model):
  203. def __init__(self):
  204. """ Shared by a mouse-handler and the ui
  205. """
  206. ## Emits selected spawn id
  207. self.changed = Signal(int)
  208. self._selectedId = -1
  209. @property
  210. def selectedId(self): return self._selectedId
  211. @selectedId.setter
  212. def selectedId(self, id):
  213. if self.selectedNPC:
  214. self.selectedNPC.hasDied.disconnect(self._removeSelection)
  215. self._selectedId = id
  216. if id > 0:
  217. self.selectedNPC.hasDied.connect(self._removeSelection)
  218. self.changed.emit(id)
  219. @property
  220. def selectedNPC(self):
  221. if self.selectedId:
  222. return base.world.npcsManager.getSpawn(self._selectedId)
  223. def _removeSelection(self, spawnId):
  224. self.selectedId = -1
  225. class IntModel(Model):
  226. def __init__(self, min=0, max=100, value=0, stepSize=1):
  227. """ Used for example: character / npc -level
  228. """
  229. self.valueChanged = Signal(int)
  230. Model.__init__(self)
  231. self.__min = min
  232. self.__max = max
  233. self.__value = value
  234. self.__stepSize = stepSize
  235. @property
  236. def min(self): return self.__min
  237. @property
  238. def max(self): return self.__max
  239. @property
  240. def value(self): return self.__value
  241. @value.setter
  242. def value(self, value):
  243. self.__value = max(min(value, self.max), self.min)
  244. self.valueChanged.emit(self.value)
  245. class FloatModel(IntModel):
  246. def __init__(self, min=0, max=100, value=0, stepSize=.01):
  247. """ Used for example: character / npc -stats
  248. """
  249. self.valueChanged = Signal(float)
  250. IntModel.__init__(self, min=min, max=max, value=value, stepSize=stepSize)