123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- ########################################################################
- # Hello Worlds - Libre 3D RPG game.
- # Copyright (C) 2020 CYBERDEViL
- #
- # This file is part of Hello Worlds.
- #
- # Hello Worlds is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # Hello Worlds is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
- #
- ########################################################################
- from LUIFrame import LUIFrame
- from LUILabel import LUILabel
- from LUIProgressbar import LUIProgressbar
- from LUILayouts import LUIHorizontalLayout
- from LUIVerticalLayout import LUIVerticalLayout
- from LUISprite import LUISprite
- from LUIObject import LUIObject
- from LUIInitialState import LUIInitialState
- from core.models import SpellItemModel, SpellStatus
- class UI_ActionMessages(LUIObject):
- def __init__(self, **kwargs):
- LUIObject.__init__(self, center_horizontal=True)
- self._layout = LUIVerticalLayout(parent=self)
- self._previousMessage = ""
- self._count = 0
- LUIInitialState.init(self, kwargs)
- def print(self, message, lifeTime=2):
- if message == self._previousMessage: return
- self._previousMessage = message
- self._count += 1
- label = LUILabel(message, parent=self._layout.cell(), margin=0, font_size=20, center_horizontal=True)
- taskMgr.doMethodLater(
- lifeTime,
- self.remove,
- 'removeText',
- extraArgs=[label],
- )
- def remove(self, label):
- label.parent = None
- self._count -= 1
- if not self._count: self._previousMessage = ""
- def close(self): self.parent = None
- class UI_ItemInfo(LUIFrame):
- def __init__(self, **kwargs):
- LUIFrame.__init__(self, style=LUIFrame.FS_raised, **kwargs)
- self.margin = 0
- self._model = None
- verticalLayout = LUIVerticalLayout(parent=self)
- horizontalLayout = LUIHorizontalLayout(parent=verticalLayout.cell())
- ## Labels
- leftLayout = LUIVerticalLayout(parent=horizontalLayout.cell())
- LUILabel("Name: ", parent=leftLayout.cell(), margin=0)
- LUILabel("Points: ", parent=leftLayout.cell(), margin=0)
- LUILabel("Energy: ", parent=leftLayout.cell(), margin=0)
- LUILabel("Casting time: ", parent=leftLayout.cell(), margin=0)
- LUILabel("Cooling time: ", parent=leftLayout.cell(), margin=0)
- LUILabel("Range: ", parent=leftLayout.cell(), margin=0)
- ## Values
- rightLayout = LUIVerticalLayout(parent=horizontalLayout.cell())
- self._nameValue = LUILabel("", parent=rightLayout.cell(), margin=0, color=(0.2, 0.8, 0.2))
- self._pointsValue = LUILabel("", parent=rightLayout.cell(), margin=0, color=(0.8, 0.8, 0.0))
- self._energyValue = LUILabel("", parent=rightLayout.cell(), margin=0, color=(0.8, 0.8, 0.0))
- self._castTimeValue = LUILabel("", parent=rightLayout.cell(), margin=0)
- self._coolTimeValue = LUILabel("", parent=rightLayout.cell(), margin=0)
- self._rangeValue = LUILabel("", parent=rightLayout.cell(), margin=0)
- self._descValue = LUILabel("", parent=verticalLayout.cell(), margin=0)
- self.hide()
- def setModel(self, model):
- """
- @param model:
- @type model: core.model.SpellItemModel
- """
- self._model = model
- if model:
- self._nameValue.set_text(model.name)
- self._pointsValue.set_text("{0}".format(model.data.impactPoints))
- self._energyValue.set_text("{0}".format(model.data.energyCost))
- self._castTimeValue.set_text("{0}s".format(model.data.castTime))
- self._coolTimeValue.set_text("{0}s".format(model.data.coolDown))
- self._rangeValue.set_text("{0}min {1}max".format(model.data.rangeStart, model.data.rangeEnd))
- if model.description:
- self._descValue.set_text(str(model.description))
- self._descValue.show()
- else:
- self._descValue.hide()
- else: self._modelRemoved()
- def close(self): self.parent = None
- def _modelRemoved(self):
- self._descLabel.set_text(self._descLabelFmt)
- class UI_DragItem(LUIObject):
- icons = {
- 'unknown' : "assets/icons/unknown.png",
- 'empty' : "assets/icons/overlay_0.png"
- }
- def __init__(self, *args, model=None, **kwargs):
- LUIObject.__init__(self, *args)
- self.width = 48
- self.height = 48
- self._icon = LUISprite(self, self.icons['empty'], width=self.width, height=self.height)
- self.set_pos(100,100)
- self.setModel(model)
- def setModel(self, model=None):
- if model:
- self._icon.set_texture(model.iconPath, resize=False)
- class UI_Item(LUIObject):
- ## TODO use assets path
- icons = {
- 'unknown' : "assets/icons/unknown.png",
- 'empty' : "assets/icons/overlay_0.png"
- }
- ## TODO use assets path
- overlays = {
- 'highlite' : "assets/icons/overlay_1.png",
- 'disabled' : "assets/icons/overlay_2_disabled.png"
- }
- def __init__(self, *args, **kwargs):
- # Without solid=True it doesn't receive mouse-events
- LUIObject.__init__(self, *args, solid=True)
- self.width = 48
- self.height = 48
- self._model = None
- self._isDragging = False
- self._dragStartPos = None
- self._backupParent = self.parent
- self._dragItem = None
- self.__enabled = False
- self._backupModel = None
- self._allowedDropTypes = [SpellItemModel]
- self._icon = LUISprite(self, self.icons['empty'], width=self.width, height=self.height)
- self._label = LUILabel("", parent=self, margin=0, left=0, bottom=0)
- self._overlay = None
- LUIInitialState.init(self, kwargs)
- @property
- def dropTypes(self): return self._allowedDropTypes
- def setEnabled(self, state=True):
- if state and not self._model: return False
- self.__enabled = state
- return True
- @property
- def enabled(self): return self.__enabled
- def mousePos(self):
- if base.mouseWatcherNode.hasMouse():
- mpos = base.mouseWatcherNode.getMouse()
- mousePos = (mpos.getX() * base.getAspectRatio(), mpos.getY())
- screenWidth = base.win.getProperties().getXSize()
- screenHeight = base.win.getProperties().getYSize()
- ratio = base.getAspectRatio()
- # https://stackoverflow.com/questions/929103/convert-a-number-range-to-another-range-maintaining-ratio
- left = (((mousePos[0] - -ratio) * (screenWidth - 0)) / (ratio - -ratio)) + 0
- top = (((mousePos[1] - 1) * (screenHeight - 0)) / (-1 - 1)) + 0
- return (left, top)
- return(0,0)
- def dragDistance(self):
- if self._dragStartPos:
- mouseX, mouseY = self.mousePos()
- dragX, dragY = self._dragStartPos
- return abs((dragX + dragY) - (mouseX + mouseY))
- return 0
- def _dragWatcher(self, event):
- if not self._dragItem and self.dragDistance() > 6:
- self._removeOverlay()
- self._dragItem = UI_DragItem(base.luiRegion.root, model=self.model)
- base.uiManager.dragModel = self.model
- self.unbind('mousemove')
- taskMgr.add(self._dragTask, 'dragWatcher')
- def _dragTask(self, task):
- self._dragItem.set_pos(self.mousePos())
- return task.cont
- def _stopDrag(self):
- if self._dragItem:
- taskMgr.remove('dragWatcher')
- self._dragItem.parent = None
- self._dragItem = None
- base.uiManager.dragModel = None
- def on_mouseover(self, event):
- ## Replace model with the model behind the drag.
- if base.uiManager.dragModel:
- if type(base.uiManager.dragModel) in self.dropTypes:
- self._backupModel = self._model
- self.setModel(base.uiManager.dragModel)
- return
- ## Show information box
- if self._model:
- pos = self.get_abs_pos()
- pos -= (-60,80)
- base.uiManager.spellInfoFrame.setModel(self._model)
- base.uiManager.spellInfoFrame.set_pos(pos)
- base.uiManager.spellInfoFrame.show()
- def on_mouseout(self, event):
- if self._model:
- base.uiManager.spellInfoFrame.hide()
- if base.uiManager.dragModel:
- if type(base.uiManager.dragModel) in self.dropTypes:
- self.setModel(self._backupModel)
- self._backupModel = None
- def on_mousedown(self, event=None):
- if self.enabled:
- if not base.uiManager.dragModel:
- self._dragStartPos = self.mousePos()
- self.bind('mousemove', self._dragWatcher) # uncomment to enable drag 'n drop (that isn't complete yet)
- self._addOverlay()
- def on_mouseup(self, event=None):
- self._stopDrag()
- self._removeOverlay()
- self._dragStartPos = None
- def keyDown(self):
- if self.enabled:
- self._addOverlay()
- self.trigger_event("click")
- def keyUp(self):
- if self.enabled: self._removeOverlay()
- def setOverlay(self, overlay='highlite'):
- self._removeOverlay()
- self._addOverlay(overlay=overlay)
- def _addOverlay(self, overlay='highlite'):
- if not self._overlay:
- overlayPath = self.overlays[overlay]
- self._overlay = LUISprite(self, overlayPath, width=self.width, height=self.height)
- def _removeOverlay(self):
- if self._overlay:
- self._overlay.parent = None
- self._overlay = None
- @property
- def model(self): return self._model
- def setModel(self, model):
- self._model = model
- if model:
- self._icon.set_texture(model.iconPath, resize=False)
- self._label.set_text(model.name)
- self.setEnabled(True)
- else:
- self._modelRemoved()
- self.setEnabled(False)
- def _modelRemoved(self):
- self._icon.set_texture(self.icons['empty'], resize=False)
- self._label.set_text("")
- class UI_SpellItem(UI_Item):
- def __init__(self, *args, **kwargs):
- UI_Item.__init__(self, *args, **kwargs)
- self.castBar = None
- def _spellStatusChanged(self, status):
- """
- @param status: SpellStatus.*
- @type status: int
- """
- if status == SpellStatus.none or status == SpellStatus.done:
- self._removeOverlay()
- self.setEnabled(True)
- elif status == SpellStatus.casting:
- self.setEnabled(False)
- self.setOverlay('disabled')
- self.castBar = UI_CastingBar(self.model.data.castTime, parent=base.luiRegion.root)
- self.castBar.start()
- elif status == SpellStatus.cooling:
- self.setEnabled(False)
- self.setOverlay('disabled')
- elif status == SpellStatus.cancelled:
- if self.castBar: self.castBar.cancel()
- def setModel(self, model):
- """
- @param model:
- @type model: core.models.SpellItemModel
- """
- if self._model:
- self._model.statusChanged.disconnect(self._spellStatusChanged)
- UI_Item.setModel(self, model)
- if model:
- self._spellStatusChanged(model.status)
- model.statusChanged.connect(self._spellStatusChanged)
- class UI_ProgressBar(LUIObject):
- def __init__(self, **kwargs):
- LUIObject.__init__(self)
- self.width = 200
- self.height = 10
- self._layout = LUIVerticalLayout(parent=self)
- self._bar = LUIProgressbar(parent=self._layout.cell(), width=200, label_fmt="casting")
- LUIInitialState.init(self, kwargs)
- def __bool__(self):
- return self.is_visible()
- def close(self, event=None): self.parent = None
- class UI_CastingBar(UI_ProgressBar):
- def __init__(self, castingTime, **kwargs):
- UI_ProgressBar.__init__(self, **kwargs)
- self.center_horizontal = True
- self.bottom = 125
- self._cancelled = False
- self.castingTime = castingTime
- self._bar.set_max(castingTime)
- def start(self):
- self._bar.set_value(0)
- # TODO investigate where this weird 0.25 margin comes from.
- # it's only visual
- self._bar.set_max(self.castingTime - 0.25)
- ## Start a task that updates the progress-bar over time.
- taskMgr.doMethodLater(
- 0.1,
- self.updateProgress,
- 'player_castBar'
- )
- def cancel(self):
- self._cancelled = True
- def updateProgress(self, task):
- """ Task method that updates the progress-bar over time.
- """
- if not self._cancelled and self._bar.get_value() < self._bar.get_max():
- self._bar.set_value(self._bar.get_value() + 0.1)
- return task.again
- self.close()
- return task.done
- class UI_SpellBar(LUIFrame):
- def __init__(self, **kwargs):
- LUIFrame.__init__(self, style=LUIFrame.FS_sunken, **kwargs)
- self.height = 68
- self.margin = 0
- screenHeight = base.win.getProperties().getYSize()
- self.top = screenHeight - 68 - 3
- self.center_horizontal = True
- self._layout = LUIHorizontalLayout(parent=self)
- self._layout.spacing = 3
- self.maxItems = 12
- self._items = []
- ## List with spellId's; should be same ordered as self._items!
- for i in range(0, self.maxItems):
- item = UI_SpellItem(self._layout.cell())
- self._items.append(item)
- item.bind('click', self._spellClicked)
- # Assing shortcut keys 0 - 9
- keyMap = [
- ["1", 0],
- ["2", 1],
- ["3", 2],
- ["4", 3],
- ["5", 4],
- ["6", 5],
- ["7", 6],
- ["8", 7],
- ["9", 8],
- ["0", 9],
- ]
- for key, index in keyMap:
- base.accept(key, self._items[index].keyDown)
- base.accept('{0}-up'.format(key), self._items[index].keyUp)
- base.accept('WINDOW_RESIZED', self.reposition)
- def clear(self):
- for item in self._items: item.setModel(None)
- def characterChanged(self):
- self.clear()
- # TODO
- if base.world.playerController.character:
- count = 0
- for spell in base.world.playerController.character.characterData.spells:
- model = SpellItemModel(spell)
- self._items[count].setModel(model)
- if count == self.maxItems: break
- count += 1
- def _spellClicked(self, event):
- index = self._items.index(event.sender)
- if self._items[index].model:
- selectedSpawnId = base.world.npcsManager.selectedNpcModel.selectedId
- base.world.playerController.character.startCast(
- self._items[index].model.data.id,
- selectedSpawnId
- )
- def reposition(self):
- self.top = base.win.getProperties().getYSize() - self.height - 10
- def close(self): self.parent = None
- class UI_PlayerInfoBox(LUIFrame):
- def __init__(self, parent=None, top=10, left=10):
- if not parent: parent = base.luiRegion.root
- LUIFrame.__init__(self, parent=parent, style=LUIFrame.FS_sunken, width=175, height=80, margin=0, top=top, left=left)
- self._layout = LUIVerticalLayout(self)
- self.nameLabel = LUILabel(parent=self._layout.cell(), text="Name: ")
- self.healthBar = LUIProgressbar(parent=self._layout.cell(), width=145, label_fmt="Health: {value}/{max}")
- self.energyBar = LUIProgressbar(parent=self._layout.cell(), width=145, label_fmt="Energy: {value}/{max}")
- self._currentCharacterData = None
- def characterChanged(self):
- if self._currentCharacterData:
- self._currentCharacterData.stats.health.valueChanged.disconnect(self._healthChanged)
- self._currentCharacterData.stats.energy.valueChanged.disconnect(self._energyChanged)
- self._currentCharacterData = None
- if base.world.playerController.character:
- self._currentCharacterData = base.world.playerController.character.characterData
- self.nameLabel.set_text(self._currentCharacterData.name)
- self.healthBar.set_max(self._currentCharacterData.stats.health.max)
- self.healthBar.set_value(self._currentCharacterData.stats.health.value)
- self.energyBar.set_max(self._currentCharacterData.stats.energy.max)
- self.energyBar.set_value(self._currentCharacterData.stats.energy.value)
- self._currentCharacterData.stats.health.valueChanged.connect(self._healthChanged)
- self._currentCharacterData.stats.energy.valueChanged.connect(self._energyChanged)
- def _healthChanged(self, value): self.healthBar.set_value(value)
- def _energyChanged(self, value): self.energyBar.set_value(value)
- def close(self): self.parent = None
- class UI_CreatureInfoBox:
- def __init__(self, parent=None, top=100, left=10):
- if not parent: parent = base.luiRegion.root
- self._currentSpawnId = -1
- self._container = LUIFrame(parent=parent, width=175, height=55, style=LUIFrame.FS_sunken, margin=0, top=top, left=left)
- self.nameLabel = LUILabel(parent=self._container, text="Name: ", top=0)
- self.healthBar = LUIProgressbar(parent=self._container, width=145, top=20, label_fmt="Health: {value}/{max}")
- base.world.npcsManager.selectedNpcModel.changed.connect(self._selectionChanged)
- self.hide()
- def hasMouse(self): return self._container.hasMouse()
- def _selectionChanged(self, spawnId):
- # Disconnect previously set connections
- if self._currentSpawnId != -1 and self._currentSpawnId != spawnId:
- oldNpc = base.world.npcsManager.getSpawn(self._currentSpawnId) # creaturesManager.Creature
- if oldNpc:
- oldNpc.characterData.stats.health.valueChanged.disconnect(self._healthChanged)
- oldNpc.removeSelectPlane()
- # Hide if selected id is 0 or smaller
- if spawnId < 1:
- self.hide()
- self._currentSpawnId = -1
- return
- if self._currentSpawnId != spawnId:
- self.show()
- npc = base.world.npcsManager.getSpawn(spawnId) # creaturesManager.Creature
- npc.addSelectPlane()
- # Name
- self.nameLabel.text = "{0} (id: {1} lvl: {2})".format(npc.characterData.name, npc.characterData.spawnData.id, npc.characterData.stats.level.value)
- # Health
- self.healthBar.set_max(npc.characterData.stats.health.max)
- self.healthBar.set_value(npc.characterData.stats.health.value)
- self._currentSpawnId = spawnId
- npc.characterData.stats.health.valueChanged.connect(self._healthChanged)
- def _healthChanged(self, value):
- npc = base.world.npcsManager.getSpawn(self._currentSpawnId)
- if npc: self.healthBar.set_value(npc.characterData.stats.health.value)
- def close(self):
- self._container.parent = None
- self._container = None
- def hide(self):
- self._container.hide()
- def show(self):
- self._container.show()
- def isVisible(self): return self._container.visible
|