def _start_subprocess(self, *args): if hasattr(self, '_started'): raise ChildProcessError("Subprocess already running") config = self.config self.procman = EngineProcessManager() enkw = {'logger': Logger} if config['LiSE'].get('logfile'): enkw['logfile'] = config['LiSE']['logfile'] if config['LiSE'].get('loglevel'): enkw['loglevel'] = config['LiSE']['loglevel'] self.engine = self.procman.start(config['LiSE']['world'], **enkw) self.pull_time() self.engine.time.connect(self._pull_time_from_signal, weak=False) self.engine.string.language.connect(self._pull_lang, weak=False) self.engine.character.connect(self._pull_chars, weak=False) self.bind(branch=self._push_time, turn=self._push_time, tick=self._push_time) char = config['ELiDE']['boardchar'] if char not in self.engine.character: self.engine.add_character(char) self._started = True
def setUp(self): self.manager = EngineProcessManager() self.tempdir = tempfile.mkdtemp(dir='.') self.engine = self.manager.start(self.tempdir, connect_string='sqlite:///:memory:') self.graphmakers = (self.engine.new_character, ) self.addCleanup(self._do_cleanup)
def _start_subprocess(self, *args): config = self.config self.procman = EngineProcessManager() enkw = {'logger': Logger} if config['LiSE'].get('logfile'): enkw['logfile'] = config['LiSE']['logfile'] if config['LiSE'].get('loglevel'): enkw['loglevel'] = config['LiSE']['loglevel'] self.engine = self.procman.start(config['LiSE']['world'], config['LiSE']['code'], **enkw) self.pull_time() @self.engine.time.connect def pull_time(inst, **kwargs): self.branch = inst.branch self.tick = inst.tick @self.engine.string.language.connect def pull_lang(inst, **kwargs): self.strings.language = kwargs['language'] @self.engine.character.connect def pull_chars(*args): self.chars.names = list(self.engine.character) self.bind(branch=self._push_time, tick=self._push_time) char = config['ELiDE']['boardchar'] if char not in self.engine.character: self.engine.add_character(char)
def start_subprocess(self, *args): """Start the LiSE core and get a proxy to it Must be called before ``init_board`` """ if hasattr(self, '_started'): raise ChildProcessError("Subprocess already running") config = self.config enkw = {'logger': Logger} if config['LiSE'].get('logfile'): enkw['logfile'] = config['LiSE']['logfile'] if config['LiSE'].get('loglevel'): enkw['loglevel'] = config['LiSE']['loglevel'] self.procman = EngineProcessManager() self.engine = engine = self.procman.start(**enkw) self.pull_time() self.engine.time.connect(self._pull_time_from_signal, weak=False) self.engine.string.language.connect(self._pull_lang, weak=False) self.engine.character.connect(self._pull_chars, weak=False) self.bind(branch=self._push_time, turn=self._push_time, tick=self._push_time) self.strings.store = self.engine.string self._started = True return engine
def setUp(self): self.manager = EngineProcessManager() self.engine = self.manager.start('sqlite:///:memory:') self.graphmakers = (self.engine.new_character, ) self.tempdir = tempfile.mkdtemp(dir='.') for f in ('trigger.py', 'prereq.py', 'action.py', 'function.py', 'method.py', 'strings.json'): if os.path.exists(f): os.rename(f, os.path.join(self.tempdir, f))
class ProxyTest(LiSE.allegedb.tests.test_all.AllegedTest): def setUp(self): self.manager = EngineProcessManager() self.tempdir = tempfile.mkdtemp(dir='.') self.engine = self.manager.start(self.tempdir, connect_string='sqlite:///:memory:') self.graphmakers = (self.engine.new_character, ) self.addCleanup(self._do_cleanup) def _do_cleanup(self): self.manager.shutdown() shutil.rmtree(self.tempdir)
def _start_subprocess(self, *args): if hasattr(self, '_started'): raise ChildProcessError("Subprocess already running") config = self.config self.procman = EngineProcessManager() enkw = {'logger': Logger} if config['LiSE'].get('logfile'): enkw['logfile'] = config['LiSE']['logfile'] if config['LiSE'].get('loglevel'): enkw['loglevel'] = config['LiSE']['loglevel'] self.engine = self.procman.start( config['LiSE']['world'], **enkw ) self.pull_time() self.engine.time.connect(self._pull_time_from_signal, weak=False) self.engine.string.language.connect(self._pull_lang, weak=False) self.engine.character.connect(self._pull_chars, weak=False) self.bind( branch=self._push_time, turn=self._push_time, tick=self._push_time ) char = config['ELiDE']['boardchar'] if char not in self.engine.character: self.engine.add_character(char) self._started = True
class ProxyTest(allegedb.tests.test_all.AllegedTest): def setUp(self): self.manager = EngineProcessManager() self.engine = self.manager.start('sqlite:///:memory:') self.graphmakers = (self.engine.new_character, ) self.tempdir = tempfile.mkdtemp(dir='.') for f in ('trigger.py', 'prereq.py', 'action.py', 'function.py', 'method.py', 'strings.json'): if os.path.exists(f): os.rename(f, os.path.join(self.tempdir, f)) def tearDown(self): self.manager.shutdown() for f in ('trigger.py', 'prereq.py', 'action.py', 'function.py', 'method.py', 'strings.json'): if os.path.exists(f): os.remove(f) if os.path.exists(os.path.join(self.tempdir, f)): os.rename(os.path.join(self.tempdir, f), f) os.rmdir(self.tempdir)
def setUp(self): self.manager = EngineProcessManager() self.engine = self.manager.start('sqlite:///:memory:') self.graphmakers = (self.engine.new_character,) self.tempdir = tempfile.mkdtemp(dir='.') for f in ( 'trigger.py', 'prereq.py', 'action.py', 'function.py', 'method.py', 'strings.json' ): if os.path.exists(f): os.rename(f, os.path.join(self.tempdir, f))
def _start_subprocess(self, *args): config = self.config self.procman = EngineProcessManager() enkw = {'logger': Logger, 'validate': True} if config['LiSE'].get('logfile'): enkw['logfile'] = config['LiSE']['logfile'] if config['LiSE'].get('loglevel'): enkw['loglevel'] = config['LiSE']['loglevel'] self.engine = self.procman.start(config['LiSE']['world'], **enkw) self.pull_time() self.engine.time.connect(self._pull_time_from_signal, weak=False) self.engine.string.language.connect(self._pull_lang, weak=False) self.engine.character.connect(self._pull_chars, weak=False) self.bind(branch=self._push_time, turn=self._push_time, tick=self._push_time) char = config['ELiDE']['boardchar'] if char not in self.engine.character: self.engine.add_character(char)
class ProxyTest(allegedb.tests.test_all.AllegedTest): def setUp(self): self.manager = EngineProcessManager() self.engine = self.manager.start('sqlite:///:memory:') self.graphmakers = (self.engine.new_character,) self.tempdir = tempfile.mkdtemp(dir='.') for f in ( 'trigger.py', 'prereq.py', 'action.py', 'function.py', 'method.py', 'strings.json' ): if os.path.exists(f): os.rename(f, os.path.join(self.tempdir, f)) def tearDown(self): self.manager.shutdown() for f in ( 'trigger.py', 'prereq.py', 'action.py', 'function.py', 'method.py', 'strings.json' ): if os.path.exists(f): os.remove(f) if os.path.exists(os.path.join(self.tempdir, f)): os.rename(os.path.join(self.tempdir, f), f) os.rmdir(self.tempdir)
class ELiDEApp(App): """Extensible LiSE Development Environment. """ title = 'ELiDE' engine = ObjectProperty() branch = StringProperty('trunk') turn = NumericProperty(0) tick = NumericProperty(0) character = ObjectProperty() selection = ObjectProperty(None, allownone=True) selected_proxy = ObjectProperty() def on_selection(self, *args): Logger.debug("App: {} selected".format(self.selection)) def _get_character_name(self, *args): if self.character is None: return return self.character.name def _set_character_name(self, name): if self.character.name != name: self.character = self.engine.character[name] character_name = AliasProperty(_get_character_name, _set_character_name, bind=('character', )) def _pull_time(self, *args): if not self.engine: Clock.schedule_once(self._pull_time, 0) return branch, turn, tick = self.engine.btt() self.branch = branch self.turn = turn self.tick = tick pull_time = trigger(_pull_time) @trigger def _push_time(self, *args): branch, turn, tick = self.engine.btt() if (self.branch, self.turn, self.tick) != (branch, turn, tick): self.engine.time_travel( self.branch, self.turn, self.tick if self.tick != tick else None, chars=[self.character.name], cb=self.mainscreen._update_from_time_travel) def set_tick(self, t): """Set my tick to the given value, cast to an integer.""" self.tick = int(t) def set_turn(self, t): self.turn = int(t) def select_character(self, char): """Change my ``character`` to the selected character object if they aren't the same. """ if char == self.character: return self.character = char def build_config(self, config): """Set config defaults""" for sec in 'LiSE', 'ELiDE': config.adddefaultsection(sec) config.setdefaults( 'LiSE', { 'world': 'sqlite:///LiSEworld.db', 'language': 'eng', 'logfile': '', 'loglevel': 'info' }) config.setdefaults( 'ELiDE', { 'boardchar': 'physical', 'debugger': 'no', 'inspector': 'no', 'user_kv': 'yes', 'play_speed': '1', 'thing_graphics': json.dumps( [("Marsh Davies' Island", 'marsh_davies_island_fg.atlas'), ('RLTiles: Body', 'base.atlas'), ('RLTiles: Basic clothes', 'body.atlas'), ('RLTiles: Armwear', 'arm.atlas'), ('RLTiles: Legwear', 'leg.atlas'), ('RLTiles: Right hand', 'hand1.atlas'), ('RLTiles: Left hand', 'hand2.atlas'), ('RLTiles: Boots', 'boot.atlas'), ('RLTiles: Hair', 'hair.atlas'), ('RLTiles: Beard', 'beard.atlas'), ('RLTiles: Headwear', 'head.atlas')]), 'place_graphics': json.dumps( [("Marsh Davies' Island", 'marsh_davies_island_bg.atlas'), ("Marsh Davies' Crypt", 'marsh_davies_crypt.atlas'), ('RLTiles: Dungeon', 'dungeon.atlas')]) }) config.write() def build(self): """Make sure I can use the database, create the tables as needed, and return the root widget. """ self.icon = 'icon_24px.png' config = self.config Logger.debug("ELiDEApp: starting with world {}, path {}".format( config['LiSE']['world'], LiSE.__path__[-1])) if config['ELiDE']['debugger'] == 'yes': import pdb pdb.set_trace() self.manager = ScreenManager(transition=NoTransition()) if config['ELiDE']['inspector'] == 'yes': from kivy.core.window import Window from kivy.modules import inspector inspector.create_inspector(Window, self.manager) self._start_subprocess() self._add_screens() return self.manager def _pull_lang(self, *args, **kwargs): self.strings.language = kwargs['language'] def _pull_chars(self, *args, **kwargs): self.chars.names = list(self.engine.character) def _pull_time_from_signal(self, *args, branch, turn, tick): self.branch, self.turn, self.tick = branch, turn, tick def _start_subprocess(self, *args): if hasattr(self, '_started'): raise ChildProcessError("Subprocess already running") config = self.config self.procman = EngineProcessManager() enkw = {'logger': Logger} if config['LiSE'].get('logfile'): enkw['logfile'] = config['LiSE']['logfile'] if config['LiSE'].get('loglevel'): enkw['loglevel'] = config['LiSE']['loglevel'] self.engine = self.procman.start(config['LiSE']['world'], **enkw) self.pull_time() self.engine.time.connect(self._pull_time_from_signal, weak=False) self.engine.string.language.connect(self._pull_lang, weak=False) self.engine.character.connect(self._pull_chars, weak=False) self.bind(branch=self._push_time, turn=self._push_time, tick=self._push_time) char = config['ELiDE']['boardchar'] if char not in self.engine.character: self.engine.add_character(char) self._started = True def _add_screens(self, *args): if not getattr(self, '_started'): Clock.schedule_once(self._add_screens, 0) return def toggler(screenname): def tog(*args): if self.manager.current == screenname: self.manager.current = 'main' else: self.manager.current = screenname return tog config = self.config self.pawncfg = ELiDE.spritebuilder.PawnConfigScreen( toggle=toggler('pawncfg'), data=json.loads(config['ELiDE']['thing_graphics'])) self.spotcfg = ELiDE.spritebuilder.SpotConfigScreen( toggle=toggler('spotcfg'), data=json.loads(config['ELiDE']['place_graphics'])) self.rules = ELiDE.rulesview.RulesScreen(engine=self.engine, toggle=toggler('rules')) self.charrules = ELiDE.rulesview.CharacterRulesScreen( engine=self.engine, character=self.character, toggle=toggler('charrules')) self.bind(character=self.charrules.setter('character')) self.chars = ELiDE.charsview.CharactersScreen( engine=self.engine, toggle=toggler('chars'), names=list(self.engine.character), new_board=self.new_board) self.bind(character_name=self.chars.setter('character_name')) def chars_push_character_name(*args): self.unbind(character_name=self.chars.setter('character_name')) self.character_name = self.chars.character_name self.bind(character_name=self.chars.setter('character_name')) self.chars.push_character_name = chars_push_character_name self.strings = ELiDE.stores.StringsEdScreen( language=self.engine.string.language, language_setter=self._set_language, toggle=toggler('strings')) self.funcs = ELiDE.stores.FuncsEdScreen(name='funcs', toggle=toggler('funcs')) self.select_character( self.engine.character[config['ELiDE']['boardchar']]) self.statcfg = ELiDE.statcfg.StatScreen(toggle=toggler('statcfg'), engine=self.engine) self.bind(selected_proxy=self.statcfg.setter('proxy')) self.mainscreen = ELiDE.screen.MainScreen( use_kv=config['ELiDE']['user_kv'] == 'yes', play_speed=int(config['ELiDE']['play_speed']), boards={ name: Board(character=char) for name, char in self.engine.character.items() }) if self.mainscreen.statlist: self.statcfg.statlist = self.mainscreen.statlist self.mainscreen.bind(statlist=self.statcfg.setter('statlist')) self.bind(selection=self.refresh_selected_proxy, character=self.refresh_selected_proxy) self.selected_proxy = self._get_selected_proxy() for wid in (self.mainscreen, self.pawncfg, self.spotcfg, self.statcfg, self.rules, self.charrules, self.chars, self.strings, self.funcs): self.manager.add_widget(wid) def _set_language(self, lang): self.engine.string.language = lang def _get_selected_proxy(self): if self.selection is None: return self.character.stat elif hasattr(self.selection, 'proxy'): return self.selection.proxy elif (hasattr(self.selection, 'portal') and self.selection.portal is not None): return self.selection.portal else: raise ValueError("Invalid selection: {}".format(self.selection)) def refresh_selected_proxy(self, *args): self.selected_proxy = self._get_selected_proxy() def on_character_name(self, *args): if self.config['ELiDE']['boardchar'] != self.character_name: self.config['ELiDE']['boardchar'] = self.character_name def on_character(self, *args): if not hasattr(self, 'mainscreen'): Clock.schedule_once(self.on_character, 0) return if hasattr(self, '_oldchar'): self.mainscreen.boards[self._oldchar.name].unbind( selection=self.setter('selection')) self.selection = None self.mainscreen.boards[self.character.name].bind( selection=self.setter('selection')) def on_pause(self): """Sync the database with the current state of the game.""" self.engine.commit() self.strings.save() self.funcs.save() self.config.write() def on_stop(self, *largs): """Sync the database, wrap up the game, and halt.""" self.strings.save() self.funcs.save() self.engine.commit() self.procman.shutdown() self.config.write() def delete_selection(self): """Delete both the selected widget and whatever it represents.""" selection = self.selection if selection is None: return if isinstance(selection, ArrowWidget): self.mainscreen.boardview.board.rm_arrow( selection.origin.name, selection.destination.name) selection.portal.delete() elif isinstance(selection, Spot): self.mainscreen.boardview.board.rm_spot(selection.name) selection.proxy.delete() else: assert isinstance(selection, Pawn) self.mainscreen.boardview.board.rm_pawn(selection.name) selection.proxy.delete() self.selection = None def new_board(self, name): """Make a board for a character name, and switch to it.""" char = self.engine.character[name] board = Board(character=char) self.mainscreen.boards[name] = board self.character = char
from LiSE.proxy import EngineProcessManager procman = EngineProcessManager() eng = procman.start(':memory:') phys = eng.new_character('physical') place = phys.new_place('place1') place['heresy'] = True assert place['heresy'] eng.tick = 1 assert place['heresy'] place['heresy'] = False assert not place['heresy'] eng.tick = 0 assert place['heresy'] eng.tick = 1 assert not place['heresy']
class ELiDEApp(App): """Extensible LiSE Development Environment. """ title = 'ELiDE' engine = ObjectProperty() branch = StringProperty('trunk') turn = NumericProperty(0) tick = NumericProperty(0) character = ObjectProperty() selection = ObjectProperty(None, allownone=True) selected_proxy = ObjectProperty() def on_selection(self, *args): Logger.debug("App: {} selected".format(self.selection)) def _get_character_name(self, *args): if self.character is None: return return self.character.name def _set_character_name(self, name): if self.character.name != name: self.character = self.engine.character[name] character_name = AliasProperty( _get_character_name, _set_character_name, bind=('character',) ) def _pull_time(self, *args): if not self.engine: Clock.schedule_once(self._pull_time, 0) return branch, turn, tick = self.engine._btt() self.branch = branch self.turn = turn self.tick = tick pull_time = trigger(_pull_time) @trigger def _push_time(self, *args): branch, turn, tick = self.engine._btt() if (self.branch, self.turn, self.tick) != (branch, turn, tick): self.engine.time_travel( self.branch, self.turn, self.tick if self.tick != tick else None, chars=[self.character.name], cb=self.mainscreen._update_from_time_travel ) def set_tick(self, t): """Set my tick to the given value, cast to an integer.""" self.tick = int(t) def set_turn(self, t): self.turn = int(t) def select_character(self, char): """Change my ``character`` to the selected character object if they aren't the same. """ if char == self.character: return self.character = char def build_config(self, config): """Set config defaults""" for sec in 'LiSE', 'ELiDE': config.adddefaultsection(sec) config.setdefaults( 'LiSE', { 'world': 'sqlite:///LiSEworld.db', 'language': 'eng', 'logfile': '', 'loglevel': 'info' } ) config.setdefaults( 'ELiDE', { 'boardchar': 'physical', 'debugger': 'no', 'inspector': 'no', 'user_kv': 'yes', 'play_speed': '1', 'thing_graphics': json.dumps([ ("Marsh Davies' Island", 'marsh_davies_island_fg.atlas'), ('RLTiles: Body', 'base.atlas'), ('RLTiles: Basic clothes', 'body.atlas'), ('RLTiles: Armwear', 'arm.atlas'), ('RLTiles: Legwear', 'leg.atlas'), ('RLTiles: Right hand', 'hand1.atlas'), ('RLTiles: Left hand', 'hand2.atlas'), ('RLTiles: Boots', 'boot.atlas'), ('RLTiles: Hair', 'hair.atlas'), ('RLTiles: Beard', 'beard.atlas'), ('RLTiles: Headwear', 'head.atlas') ]), 'place_graphics': json.dumps([ ("Marsh Davies' Island", 'marsh_davies_island_bg.atlas'), ("Marsh Davies' Crypt", 'marsh_davies_crypt.atlas'), ('RLTiles: Dungeon', 'dungeon.atlas') ]) } ) config.write() def build(self): """Make sure I can use the database, create the tables as needed, and return the root widget. """ self.icon = 'icon_24px.png' config = self.config Logger.debug( "ELiDEApp: starting with world {}, path {}".format( config['LiSE']['world'], LiSE.__path__[-1] ) ) if config['ELiDE']['debugger'] == 'yes': import pdb pdb.set_trace() self.manager = ScreenManager(transition=NoTransition()) if config['ELiDE']['inspector'] == 'yes': from kivy.core.window import Window from kivy.modules import inspector inspector.create_inspector(Window, self.manager) self._start_subprocess() self._add_screens() return self.manager def _pull_lang(self, *args, **kwargs): self.strings.language = kwargs['language'] def _pull_chars(self, *args, **kwargs): self.chars.names = list(self.engine.character) def _pull_time_from_signal(self, *args, branch, turn, tick): self.branch, self.turn, self.tick = branch, turn, tick def _start_subprocess(self, *args): if hasattr(self, '_started'): raise ChildProcessError("Subprocess already running") config = self.config self.procman = EngineProcessManager() enkw = {'logger': Logger} if config['LiSE'].get('logfile'): enkw['logfile'] = config['LiSE']['logfile'] if config['LiSE'].get('loglevel'): enkw['loglevel'] = config['LiSE']['loglevel'] self.engine = self.procman.start( config['LiSE']['world'], **enkw ) self.pull_time() self.engine.time.connect(self._pull_time_from_signal, weak=False) self.engine.string.language.connect(self._pull_lang, weak=False) self.engine.character.connect(self._pull_chars, weak=False) self.bind( branch=self._push_time, turn=self._push_time, tick=self._push_time ) char = config['ELiDE']['boardchar'] if char not in self.engine.character: self.engine.add_character(char) self._started = True def _add_screens(self, *args): if not getattr(self, '_started'): Clock.schedule_once(self._add_screens, 0) return def toggler(screenname): def tog(*args): if self.manager.current == screenname: self.manager.current = 'main' else: self.manager.current = screenname return tog config = self.config self.pawncfg = ELiDE.spritebuilder.PawnConfigScreen( toggle=toggler('pawncfg'), data=json.loads(config['ELiDE']['thing_graphics']) ) self.spotcfg = ELiDE.spritebuilder.SpotConfigScreen( toggle=toggler('spotcfg'), data=json.loads(config['ELiDE']['place_graphics']) ) self.rules = ELiDE.rulesview.RulesScreen( engine=self.engine, toggle=toggler('rules') ) self.charrules = ELiDE.rulesview.CharacterRulesScreen( engine=self.engine, character=self.character, toggle=toggler('charrules') ) self.bind(character=self.charrules.setter('character')) self.chars = ELiDE.charsview.CharactersScreen( engine=self.engine, toggle=toggler('chars'), names=list(self.engine.character), new_board=self.new_board ) self.bind(character_name=self.chars.setter('character_name')) def chars_push_character_name(*args): self.unbind(character_name=self.chars.setter('character_name')) self.character_name = self.chars.character_name self.bind(character_name=self.chars.setter('character_name')) self.chars.push_character_name = chars_push_character_name self.strings = ELiDE.stores.StringsEdScreen( language=self.engine.string.language, language_setter=self._set_language, toggle=toggler('strings') ) self.funcs = ELiDE.stores.FuncsEdScreen( name='funcs', toggle=toggler('funcs') ) self.select_character( self.engine.character[ config['ELiDE']['boardchar'] ] ) self.statcfg = ELiDE.statcfg.StatScreen( toggle=toggler('statcfg'), engine=self.engine ) self.bind( selected_proxy=self.statcfg.setter('proxy') ) self.mainscreen = ELiDE.screen.MainScreen( use_kv=config['ELiDE']['user_kv'] == 'yes', play_speed=int(config['ELiDE']['play_speed']), boards={ name: Board( character=char ) for name, char in self.engine.character.items() } ) if self.mainscreen.statlist: self.statcfg.statlist = self.mainscreen.statlist self.mainscreen.bind(statlist=self.statcfg.setter('statlist')) self.bind( selection=self.refresh_selected_proxy, character=self.refresh_selected_proxy ) self.selected_proxy = self._get_selected_proxy() for wid in ( self.mainscreen, self.pawncfg, self.spotcfg, self.statcfg, self.rules, self.charrules, self.chars, self.strings, self.funcs ): self.manager.add_widget(wid) def _set_language(self, lang): self.engine.string.language = lang def _get_selected_proxy(self): if self.selection is None: return self.character.stat elif hasattr(self.selection, 'proxy'): return self.selection.proxy elif ( hasattr(self.selection, 'portal') and self.selection.portal is not None ): return self.selection.portal else: raise ValueError("Invalid selection: {}".format(self.selection)) def refresh_selected_proxy(self, *args): self.selected_proxy = self._get_selected_proxy() def on_character_name(self, *args): if self.config['ELiDE']['boardchar'] != self.character_name: self.config['ELiDE']['boardchar'] = self.character_name def on_character(self, *args): if not hasattr(self, 'mainscreen'): Clock.schedule_once(self.on_character, 0) return if hasattr(self, '_oldchar'): self.mainscreen.boards[self._oldchar.name].unbind(selection=self.setter('selection')) self.selection = None self.mainscreen.boards[self.character.name].bind(selection=self.setter('selection')) def on_pause(self): """Sync the database with the current state of the game.""" self.engine.commit() self.strings.save() self.funcs.save() self.config.write() def on_stop(self, *largs): """Sync the database, wrap up the game, and halt.""" self.strings.save() self.funcs.save() self.engine.commit() self.procman.shutdown() self.config.write() def delete_selection(self): """Delete both the selected widget and whatever it represents.""" selection = self.selection if selection is None: return if isinstance(selection, ArrowWidget): self.mainscreen.boardview.board.rm_arrow( selection.origin.name, selection.destination.name ) selection.portal.delete() elif isinstance(selection, Spot): self.mainscreen.boardview.board.rm_spot(selection.name) selection.proxy.delete() else: assert isinstance(selection, Pawn) self.mainscreen.boardview.board.rm_pawn(selection.name) selection.proxy.delete() self.selection = None def new_board(self, name): """Make a board for a character name, and switch to it.""" char = self.engine.character[name] board = Board(character=char) self.mainscreen.boards[name] = board self.character = char
class ELiDEApp(App): """Extensible LiSE Development Environment. """ title = 'ELiDE' engine = ObjectProperty() branch = StringProperty('trunk') turn = NumericProperty(0) tick = NumericProperty(0) character = ObjectProperty() selection = ObjectProperty(None, allownone=True) selected_proxy = ObjectProperty() selected_proxy_name = StringProperty('') statcfg = ObjectProperty() def on_selection(self, *args): Logger.debug("App: {} selected".format(self.selection)) def on_selected_proxy(self, *args): if hasattr(self.selected_proxy, 'name'): self.selected_proxy_name = str(self.selected_proxy.name) return selected_proxy = self.selected_proxy assert hasattr(selected_proxy, 'origin'), '{} has no origin'.format( type(selected_proxy)) assert hasattr(selected_proxy, 'destination'), '{} has no destination'.format( type(selected_proxy)) origin = selected_proxy.origin destination = selected_proxy.destination reciprocal = selected_proxy.reciprocal if selected_proxy.get( 'is_mirror', False) or (reciprocal and reciprocal.get('is_mirror', False)): link = '<>' else: link = '->' self.selected_proxy_name = str(origin.name) + link + str( destination.name) def _get_character_name(self, *args): if self.character is None: return return self.character.name def _set_character_name(self, name): if self.character.name != name: self.character = self.engine.character[name] character_name = AliasProperty(_get_character_name, _set_character_name, bind=('character', )) def _pull_time(self, *args): if not self.engine: Clock.schedule_once(self._pull_time, 0) return branch, turn, tick = self.engine._btt() self.branch = branch self.turn = turn self.tick = tick pull_time = trigger(_pull_time) @trigger def _push_time(self, *args): branch, turn, tick = self.engine._btt() if (self.branch, self.turn, self.tick) != (branch, turn, tick): self.engine.time_travel( self.branch, self.turn, self.tick if self.tick != tick else None, chars=[self.character.name], cb=self.mainscreen._update_from_time_travel) def set_tick(self, t): """Set my tick to the given value, cast to an integer.""" self.tick = int(t) def set_turn(self, t): """Set the turn to the given value, cast to an integer""" self.turn = int(t) def select_character(self, char): """Change my ``character`` to the selected character object if they aren't the same. """ if char == self.character: return self.character = char def build_config(self, config): """Set config defaults""" for sec in 'LiSE', 'ELiDE': config.adddefaultsection(sec) config.setdefaults( 'LiSE', { 'world': 'sqlite:///world.db', 'language': 'eng', 'logfile': '', 'loglevel': 'info' }) config.setdefaults( 'ELiDE', { 'debugger': 'no', 'inspector': 'no', 'user_kv': 'yes', 'play_speed': '1', 'thing_graphics': json.dumps([('RLTiles: Body', 'base.atlas'), ('RLTiles: Basic clothes', 'body.atlas'), ('RLTiles: Armwear', 'arm.atlas'), ('RLTiles: Legwear', 'leg.atlas'), ('RLTiles: Right hand', 'hand1.atlas'), ('RLTiles: Left hand', 'hand2.atlas'), ('RLTiles: Boots', 'boot.atlas'), ('RLTiles: Hair', 'hair.atlas'), ('RLTiles: Beard', 'beard.atlas'), ('RLTiles: Headwear', 'head.atlas')]), 'place_graphics': json.dumps([('RLTiles: Dungeon', 'dungeon.atlas'), ('RLTiles: Floor', 'floor.atlas')]) }) config.write() def build(self): self.icon = 'icon_24px.png' config = self.config Logger.debug("ELiDEApp: starting with world {}, path {}".format( config['LiSE']['world'], LiSE.__path__[-1])) if config['ELiDE']['debugger'] == 'yes': import pdb pdb.set_trace() self.manager = ScreenManager(transition=NoTransition()) if config['ELiDE']['inspector'] == 'yes': from kivy.core.window import Window from kivy.modules import inspector inspector.create_inspector(Window, self.manager) self._add_screens() return self.manager def _pull_lang(self, *args, **kwargs): self.strings.language = kwargs['language'] def _pull_chars(self, *args, **kwargs): self.chars.names = list(self.engine.character) def _pull_time_from_signal(self, *args, branch, turn, tick): self.branch, self.turn, self.tick = branch, turn, tick def start_subprocess(self, *args): """Start the LiSE core and get a proxy to it Must be called before ``init_board`` """ if hasattr(self, '_started'): raise ChildProcessError("Subprocess already running") config = self.config enkw = {'logger': Logger} if config['LiSE'].get('logfile'): enkw['logfile'] = config['LiSE']['logfile'] if config['LiSE'].get('loglevel'): enkw['loglevel'] = config['LiSE']['loglevel'] self.procman = EngineProcessManager() self.engine = engine = self.procman.start(**enkw) self.pull_time() self.engine.time.connect(self._pull_time_from_signal, weak=False) self.engine.string.language.connect(self._pull_lang, weak=False) self.engine.character.connect(self._pull_chars, weak=False) self.bind(branch=self._push_time, turn=self._push_time, tick=self._push_time) self.strings.store = self.engine.string self._started = True return engine trigger_start_subprocess = trigger(start_subprocess) def init_board(self, *args): """Get the board widgets initialized to display the game state Must be called after start_subprocess """ if 'boardchar' not in self.engine.eternal: if 'physical' in self.engine.character: self.engine.eternal['boardchar'] = self.engine.character[ 'physical'] else: chara = self.engine.eternal[ 'boardchar'] = self.engine.new_character('physical') self.chars.names = list(self.engine.character) self.mainscreen.graphboards = { name: GraphBoard(character=char) for name, char in self.engine.character.items() } self.mainscreen.gridboards = { name: GridBoard(character=char) for name, char in self.engine.character.items() } self.select_character(self.engine.eternal['boardchar']) self.selected_proxy = self._get_selected_proxy() def _add_screens(self, *args): def toggler(screenname): def tog(*args): if self.manager.current == screenname: self.manager.current = 'main' else: self.manager.current = screenname return tog config = self.config self.mainmenu = ELiDE.menu.DirPicker(toggle=toggler('mainmenu'), start=self.start_subprocess) self.pawncfg = ELiDE.spritebuilder.PawnConfigScreen( toggle=toggler('pawncfg'), data=json.loads(config['ELiDE']['thing_graphics'])) self.spotcfg = ELiDE.spritebuilder.SpotConfigScreen( toggle=toggler('spotcfg'), data=json.loads(config['ELiDE']['place_graphics'])) self.statcfg = ELiDE.statcfg.StatScreen(toggle=toggler('statcfg'), engine=self.engine) self.rules = ELiDE.rulesview.RulesScreen(engine=self.engine, toggle=toggler('rules')) self.charrules = ELiDE.rulesview.CharacterRulesScreen( engine=self.engine, character=self.character, toggle=toggler('charrules')) self.bind(character=self.charrules.setter('character')) self.chars = ELiDE.charsview.CharactersScreen(engine=self.engine, toggle=toggler('chars'), new_board=self.new_board) self.bind(character_name=self.chars.setter('character_name')) def chars_push_character_name(*args): self.unbind(character_name=self.chars.setter('character_name')) self.character_name = self.chars.character_name self.bind(character_name=self.chars.setter('character_name')) self.chars.push_character_name = chars_push_character_name self.strings = ELiDE.stores.StringsEdScreen(toggle=toggler('strings')) self.funcs = ELiDE.stores.FuncsEdScreen(name='funcs', toggle=toggler('funcs')) self.bind(selected_proxy=self.statcfg.setter('proxy')) self.mainscreen = ELiDE.screen.MainScreen( use_kv=config['ELiDE']['user_kv'] == 'yes', play_speed=int(config['ELiDE']['play_speed'])) if self.mainscreen.statlist: self.statcfg.statlist = self.mainscreen.statlist self.mainscreen.bind(statlist=self.statcfg.setter('statlist')) self.bind(selection=self.refresh_selected_proxy, character=self.refresh_selected_proxy) for wid in (self.mainmenu, self.mainscreen, self.pawncfg, self.spotcfg, self.statcfg, self.rules, self.charrules, self.chars, self.strings, self.funcs): self.manager.add_widget(wid) self.manager.current = 'mainmenu' def update_calendar(self, calendar, past_turns=1, future_turns=5): """Fill in a calendar widget with actual simulation data""" startturn = self.turn - past_turns endturn = self.turn + future_turns stats = ['_config'] + [ stat for stat in self.selected_proxy if not stat.startswith('_') and stat not in ('character', 'name') ] if isinstance(self.selected_proxy, CharStatProxy): sched_entity = self.engine.character[self.selected_proxy.name] else: sched_entity = self.selected_proxy calendar.entity = sched_entity calendar.from_schedule(self.engine.handle('get_schedule', entity=sched_entity, stats=stats, beginning=startturn, end=endturn), start_turn=startturn) def _set_language(self, lang): self.engine.string.language = lang def _get_selected_proxy(self): if self.selection is None: return self.character.stat elif hasattr(self.selection, 'proxy'): return self.selection.proxy elif (hasattr(self.selection, 'portal') and self.selection.portal is not None): return self.selection.portal else: raise ValueError("Invalid selection: {}".format(self.selection)) def refresh_selected_proxy(self, *args): self.selected_proxy = self._get_selected_proxy() def on_character_name(self, *args): if not self.engine: Clock.schedule_once(self.on_character_name, 0) return self.engine.eternal['boardchar'] = self.engine.character[ self.character_name] def on_character(self, *args): if not hasattr(self, 'mainscreen'): Clock.schedule_once(self.on_character, 0) return if hasattr(self, '_oldchar'): self.mainscreen.graphboards[self._oldchar.name].unbind( selection=self.setter('selection')) self.mainscreen.gridboards[self._oldchar.name].unbind( selection=self.setter('selection')) self.selection = None self.mainscreen.graphboards[self.character.name].bind( selection=self.setter('selection')) self.mainscreen.gridboards[self.character.name].bind( selection=self.setter('selection')) def on_pause(self): """Sync the database with the current state of the game.""" if hasattr(self, 'engine'): self.engine.commit() self.strings.save() self.funcs.save() self.config.write() def on_stop(self, *largs): """Sync the database, wrap up the game, and halt.""" self.strings.save() self.funcs.save() if self.engine: self.engine.commit() if hasattr(self, 'procman'): self.procman.shutdown() self.config.write() def delete_selection(self): """Delete both the selected widget and whatever it represents.""" selection = self.selection if selection is None: return if isinstance(selection, GraphArrowWidget): if selection.reciprocal and selection.reciprocal.portal.get( 'is_mirror', False): selection.reciprocal.portal.delete() self.mainscreen.boardview.board.rm_arrow( selection.destination.name, selection.origin.name) self.mainscreen.boardview.board.rm_arrow( selection.origin.name, selection.destination.name) selection.portal.delete() elif isinstance(selection, GraphSpot): charn = selection.board.character.name self.mainscreen.graphboards[charn].rm_spot(selection.name) gridb = self.mainscreen.gridboards[charn] if selection.name in gridb.spot: gridb.rm_spot(selection.name) selection.proxy.delete() else: assert isinstance(selection, Pawn) charn = selection.board.character.name self.mainscreen.graphboards[charn].rm_pawn(selection.name) self.mainscreen.gridboards[charn].rm_pawn(selection.name) selection.proxy.delete() self.selection = None def new_board(self, name): """Make a graph for a character name, and switch to it.""" char = self.engine.character[name] self.mainscreen.graphboards[name] = GraphBoard(character=char) self.mainscreen.gridboards[name] = GridBoard(character=char) self.character = char