Source code for elide.charmenu

# This file is part of Elide, frontend to Lisien, a framework for life simulation games.
# Copyright (c) Zachary Spector, public@zacharyspector.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
from kivy.clock import Clock, triggered
from kivy.properties import (
	BooleanProperty,
	ObjectProperty,
	ReferenceListProperty,
)
from kivy.uix.boxlayout import BoxLayout

from lisien.proxy import CharStatProxy

from .graph.arrow import GraphArrowWidget
from .util import dummynum, load_string_once


def trigger(func):
	return triggered()(func)


[docs] class CharMenu(BoxLayout): screen = ObjectProperty() reciprocal_portal = BooleanProperty(True) revarrow = ObjectProperty(None, allownone=True) dummyplace = ObjectProperty() dummything = ObjectProperty() toggle_gridview = ObjectProperty() toggle_timestream = ObjectProperty() dummies = ReferenceListProperty(dummyplace, dummything) @property def app(self): if not self.screen: raise AttributeError("No screen, therefore no app") return self.screen.app @property def engine(self): if not self.screen or not self.screen.app: raise AttributeError("Can't get engine from screen") return self.screen.app.engine def on_screen(self, *_): if not (self.screen and self.screen.boardview and self.screen.app): Clock.schedule_once(self.on_screen, 0) return self.forearrow = GraphArrowWidget( board=self.screen.boardview.board, origin=self.ids.emptyleft, destination=self.ids.emptyright, ) self.ids.portaladdbut.add_widget(self.forearrow) self.ids.emptyleft.bind(pos=self.forearrow._trigger_repoint) self.ids.emptyright.bind(pos=self.forearrow._trigger_repoint) if self.reciprocal_portal: assert self.revarrow is None self.revarrow = GraphArrowWidget( board=self.screen.boardview.board, origin=self.ids.emptyright, destination=self.ids.emptyleft, ) self.ids.portaladdbut.add_widget(self.revarrow) self.ids.emptyleft.bind(pos=self.revarrow._trigger_repoint) self.ids.emptyright.bind(pos=self.revarrow._trigger_repoint) self.bind( reciprocal_portal=self.screen.boardview.setter("reciprocal_portal") ) def spot_from_dummy(self, dummy): if self.screen.boardview.parent != self.screen.mainview: return if dummy.collide_widget(self): return name = dummy.name self.screen.boardview.spot_from_dummy(dummy) graphboard = self.screen.graphboards[self.app.character_name] if name not in graphboard.spot: graphboard.add_spot(name) gridboard = self.screen.gridboards[self.app.character_name] if ( name not in gridboard.spot and isinstance(name, tuple) and len(name) == 2 ): gridboard.add_spot(name) def pawn_from_dummy(self, dummy): name = dummy.name if not self.screen.mainview.children[0].pawn_from_dummy(dummy): return graphboard = self.screen.graphboards[self.app.character_name] if name not in graphboard.pawn: graphboard.add_pawn(name) gridboard = self.screen.gridboards[self.app.character_name] if ( name not in gridboard.pawn and self.app.character.thing[name]["location"] in gridboard.spot ): gridboard.add_pawn(name)
[docs] def toggle_chars_screen(self, *_): """Display or hide the list you use to switch between characters.""" # TODO: update the list of chars self.app.chars.toggle()
[docs] def toggle_rules(self, *_): """Display or hide the view for constructing rules out of cards.""" if self.app.manager.current != "rules" and not isinstance( self.app.selected_proxy, CharStatProxy ): self.app.rules.entity = self.app.selected_proxy self.app.rules.rulebook = self.app.selected_proxy.rulebook if isinstance(self.app.selected_proxy, CharStatProxy): self.app.charrules.character = self.app.selected_proxy self.app.charrules.toggle() else: self.app.rules.toggle()
[docs] def toggle_funcs_editor(self): """Display or hide the text editing window for functions.""" self.app.funcs.toggle()
def toggle_strings_editor(self): self.app.strings.toggle()
[docs] def toggle_spot_cfg(self): """Show the dialog where you select graphics and a name for a place, or hide it if already showing. """ if self.app.manager.current == "spotcfg": dummyplace = self.screendummyplace self.ids.placetab.remove_widget(dummyplace) dummyplace.clear() if self.app.spotcfg.prefix: dummyplace.prefix = self.app.spotcfg.prefix dummyplace.num = ( dummynum(self.app.character, dummyplace.prefix) + 1 ) if self.app.spotcfg.imgpaths: dummyplace.paths = self.app.spotcfg.imgpaths else: dummyplace.paths = ["atlas://rltiles/floor/floor-stone"] dummyplace.center = self.ids.placetab.center self.ids.placetab.add_widget(dummyplace) else: self.app.spotcfg.prefix = self.ids.dummyplace.prefix self.app.spotcfg.toggle()
[docs] def toggle_pawn_cfg(self): """Show or hide the pop-over where you can configure the dummy pawn""" if self.app.manager.current == "pawncfg": dummything = self.app.dummything self.ids.thingtab.remove_widget(dummything) dummything.clear() if self.app.pawncfg.prefix: dummything.prefix = self.app.pawncfg.prefix dummything.num = ( dummynum(self.app.character, dummything.prefix) + 1 ) if self.app.pawncfg.imgpaths: dummything.paths = self.app.pawncfg.imgpaths else: dummything.paths = ["atlas://rltiles/base/unseen"] self.ids.thingtab.add_widget(dummything) else: self.app.pawncfg.prefix = self.ids.dummything.prefix self.app.pawncfg.toggle()
[docs] def toggle_reciprocal(self): """Flip my ``reciprocal_portal`` boolean, and draw (or stop drawing) an extra arrow on the appropriate button to indicate the fact. """ self.reciprocal_portal = ( self.screen.boardview.reciprocal_portal ) = not self.screen.boardview.reciprocal_portal if self.screen.boardview.reciprocal_portal: assert self.revarrow is None self.revarrow = GraphArrowWidget( board=self.screen.boardview.board, origin=self.ids.emptyright, destination=self.ids.emptyleft, ) self.ids.portaladdbut.add_widget(self.revarrow) self.ids.emptyright.bind(pos=self.revarrow._trigger_repoint) self.ids.emptyleft.bind(pos=self.revarrow._trigger_repoint) else: if hasattr(self, "revarrow"): self.ids.portaladdbut.remove_widget(self.revarrow) self.revarrow = None
def new_character(self, but): name = self.app.chars.ids.newname.text try: charn = self.app.engine.unpack(name) except (TypeError, ValueError): charn = name self.app.select_character(self.app.engine.new_character(charn)) self.app.chars.ids.newname.text = "" self.app.chars.charsview.adapter.data = list( self.engine.character.keys() ) Clock.schedule_once(self.toggle_chars_screen, 0.01) def on_dummyplace(self, *_): if not self.dummyplace.paths: self.dummyplace.paths = ["atlas://rltiles/floor.atlas/floor-stone"] def on_dummything(self, *_): if not self.dummything.paths: self.dummything.paths = ["atlas://rltiles/base.atlas/unseen"] @trigger def _trigger_deselect(self, *_): if hasattr(self.app.selection, "selected"): self.app.selection.selected = False self.app.selection = None
load_string_once(""" <CharMenu>: orientation: 'vertical' dummyplace: dummyplace dummything: dummything portaladdbut: portaladdbut portaldirbut: portaldirbut Button: text: 'Delete' disabled: app.edit_locked on_release: app.delete_selection() Button: id: timestreambut text: 'Timestream' disabled: app.edit_locked on_release: root.toggle_timestream() Button: id: gridviewbut text: 'Toggle grid' on_release: root.toggle_gridview() Button: text: 'Strings' disabled: app.edit_locked on_release: root.toggle_strings_editor() Button: text: 'Python' disabled: app.edit_locked on_release: root.toggle_funcs_editor() Button: text: 'Rules' disabled: app.edit_locked on_release: root.toggle_rules() Button: text: 'Characters' on_release: root.toggle_chars_screen() BoxLayout: Widget: id: placetab Dummy: id: dummyplace center: placetab.center prefix: 'place' disabled: app.edit_locked on_pos_up: root.spot_from_dummy(self) Button: text: 'cfg' disabled: app.edit_locked on_release: root.toggle_spot_cfg() BoxLayout: Widget: id: thingtab Dummy: id: dummything center: thingtab.center prefix: 'thing' disabled: app.edit_locked on_pos_up: root.pawn_from_dummy(self) Button: text: 'cfg' disabled: app.edit_locked on_release: root.toggle_pawn_cfg() BoxLayout: orientation: 'vertical' ToggleButton: id: portaladdbut disabled: app.edit_locked on_state: root._trigger_deselect() Widget: id: emptyleft center_x: portaladdbut.x + portaladdbut.width / 3 center_y: portaladdbut.center_y size: (0, 0) Widget: id: emptyright center_x: portaladdbut.right - portaladdbut.width / 3 center_y: portaladdbut.center_y size: (0, 0) Button: id: portaldirbut text: 'One-way' if root.reciprocal_portal else 'Two-way' disabled: app.edit_locked on_release: root.toggle_reciprocal() """)