class CogToFormGrid: def __init__(self, cog_h, engine, orb): self.cog_h = cog_h self.engine = engine self.orb = orb # self.track_prime_console = orb.track(TrackPrimeConsole) # self.form_grid_console = None self.cgrid = None # def on_init(self): self.cgrid = Cgrid(width=self.track_prime_console.width, height=self.track_prime_console.height) # zero_h = '%s/form_grid_console' % (self.cog_h) self.form_grid_console = solent_ext( ext='solent.ext.windows_form_grid_console', zero_h=zero_h, cb_grid_console_splat=self.cb_grid_console_splat, cb_grid_console_kevent=self.cb_grid_console_kevent, cb_grid_console_mevent=self.cb_grid_console_mevent, cb_grid_console_closed=self.cb_grid_console_closed, engine=self.engine, width=self.track_prime_console.width, height=self.track_prime_console.height) def on_term_clear(self): self.cgrid.clear() def on_term_put(self, drop, rest, s, cpair): self.cgrid.put(drop=drop, rest=rest, s=s, cpair=cpair) def on_term_render(self): self.form_grid_console.send(cgrid=self.cgrid) # def cb_grid_console_splat(self, cs_grid_console_splat): zero_h = cs_grid_console_splat.zero msg = cs_grid_console_splat.msg # self.nearcast.splat(zero_h=zero_h, msg=msg) def cb_grid_console_kevent(self, cs_grid_console_kevent): zero_h = cs_grid_console_kevent.zero_h keycode = cs_grid_console_kevent.keycode # log('received keycode %s' % (keycode)) self.nearcast.keystroke(keycode=keycode) def cb_grid_console_mevent(self, cs_grid_console_mevent): zero_h = cs_grid_console_mevent.zero_h # xxx def cb_grid_console_closed(self, cs_grid_console_closed): zero_h = cs_grid_console_closed.zero_h # xxx
class CogToGridConsole: def __init__(self, cog_h, orb, engine): self.cog_h = cog_h self.orb = orb self.engine = engine # self.track_prime = orb.track(TrackPrime) self.track_mob = orb.track(TrackMob) # self.form_grid_console = None self.cgrid = None def on_init(self): self.cgrid = Cgrid(width=self.track_prime.console_width, height=self.track_prime.console_height) # zero_h = '%s/form_grid_console' % (self.cog_h) self.form_grid_console = solent_ext( ext='solent.ext.windows_form_grid_console', zero_h=zero_h, cb_grid_console_splat=self.cb_grid_console_splat, cb_grid_console_kevent=self.cb_grid_console_kevent, cb_grid_console_mevent=self.cb_grid_console_mevent, cb_grid_console_closed=self.cb_grid_console_closed, engine=self.engine, width=self.track_prime.console_width, height=self.track_prime.console_height) def on_render(self): self.cgrid.clear() for (drop, rest, mob) in self.track_mob.top_mobs(): s = mob.c cpair = mob.cpair self.cgrid.put(drop=drop, rest=rest, s=s, cpair=cpair) self.form_grid_console.send(self.cgrid) # def cb_grid_console_splat(self, cs_grid_console_splat): zero_h = cs_grid_console_splat.zero msg = cs_grid_console_splat.msg # self.nearcast.splat(zero_h=zero_h, msg=msg) def cb_grid_console_kevent(self, cs_grid_console_kevent): zero_h = cs_grid_console_kevent.zero_h keycode = cs_grid_console_kevent.keycode # self.nearcast.grid_kevent(keycode=keycode) def cb_grid_console_mevent(self, cs_grid_console_mevent): zero_h = cs_grid_console_mevent.zero_h # xxx def cb_grid_console_closed(self, cs_grid_console_closed): zero_h = cs_grid_console_closed.zero_h # xxx
class CogToRoguebox: ''' Contains a roguelike game, and offers controls. The roguelike itself is contained to a 23x23 box in the top-left sector. Logging is offered in a box next to that. ''' def __init__(self, cog_h, engine, orb): self.cog_h = cog_h self.engine = engine self.orb = orb # self.track_prime_console = orb.track(TrackPrimeConsole) self.track_containment_mode = orb.track(TrackContainmentMode) # self.rail_roguebox = RailRoguebox() self.rail_message_feed = RailMessageFeed() self.cgrid_last = None self.cgrid_next = None self.d_keycode_to_directive = {} self.d_control_scheme = {} # (control_scheme_h, directive_h) = keycode # self.b_game_started = False self.b_mail_waiting = False self.b_refresh_needed = False def orb_turn(self, activity): if None == self.rail_roguebox: return if not self.track_containment_mode.is_focus_on_game(): return if self.b_mail_waiting: activity.mark( l=self, s='mail processing') for message in self.rail_roguebox.retrieve_mail(): self.rail_message_feed.accept( message=message, turn=self.rail_roguebox.get_turn()) self.b_mail_waiting = False self.b_refresh_needed = True # if self.b_refresh_needed: self._diff_display_refresh() self.b_refresh_needed = False # def on_init(self): console_height = self.track_prime_console.height console_width = self.track_prime_console.width # if console_height < ROGUEBOX_GAMEBOX_HEIGHT: raise Exception("console height %s too small for game height %s."%( console_height, ROGUEBOX_GAMEBOX_HEIGHT)) if console_width < ROGUEBOX_GAMEBOX_WIDTH: raise Exception("console width %s too small for game width %s."%( console_width, ROGUEBOX_GAMEBOX_WIDTH)) # rail_h = '%s/roguebox'%(self.cog_h) self.rail_roguebox.zero( rail_h=rail_h, engine=self.engine, grid_height=ROGUEBOX_GAMEBOX_HEIGHT, grid_width=ROGUEBOX_GAMEBOX_WIDTH, cb_ready_alert=self._rl_ready_alert, cb_grid_alert=self._rl_grid_alert, cb_mail_alert=self._rl_mail_alert, cb_over_alert=self._rl_over_alert) # rail_h = '%s/message_feed'%(self.cog_h) self.rail_message_feed.zero( rail_h=rail_h, height=ROGUEBOX_MFEED_HEIGHT, width=ROGUEBOX_MFEED_WIDTH, cpair_new=solent_cpair('teal'), cpair_old=solent_cpair('blue')) self.cgrid_last = Cgrid( width=console_width, height=console_height) self.cgrid_next = Cgrid( width=console_width, height=console_height) # # sequence the possible directives in the game to the core. this will # give this outer core the opportunity to match directives it # recognises to keycodes. in the future, you could imagine being able # to configure user keystrokes using this data. for directive in self.rail_roguebox.get_supported_directives(): self.nearcast.directive( directive_h=directive.h, description=directive.description) def on_keycode_to_directive(self, control_scheme_h, keycode, directive_h): self.d_keycode_to_directive[keycode] = directive_h self.d_control_scheme[ (control_scheme_h, directive_h) ] = keycode def on_x_game_ready(self): self.b_game_started = True self.nearcast.o_game_focus() def on_o_game_new(self): self.rail_message_feed.clear() self.rail_roguebox.new_game() def on_x_game_mail(self): self.b_mail_waiting = True def on_x_game_grid(self): self.b_refresh_needed = True def on_game_input(self, keycode): if keycode not in self.d_keycode_to_directive: return directive_h = self.d_keycode_to_directive[keycode] self.rail_roguebox.directive( directive_h=directive_h) self.rail_message_feed.scroll_past( turn=self.rail_roguebox.get_turn()-3) self.b_refresh_needed = True def on_o_game_focus(self): if not self.b_game_started: self.nearcast.menu_focus() return self._full_display_refresh() # def _rl_ready_alert(self): self.nearcast.x_game_ready() def _rl_grid_alert(self): self.nearcast.x_game_grid() def _rl_mail_alert(self): self.nearcast.x_game_mail() def _rl_over_alert(self): self.nearcast.x_game_over() # def _full_display_refresh(self): self.nearcast.term_clear() self.cgrid_last.clear() self._diff_display_refresh() def _diff_display_refresh(self): self.rail_roguebox.get_cgrid( cgrid=self.cgrid_next, nail=ROGUEBOX_GAMEBOX_NAIL, peri=ROGUEBOX_GAMEBOX_PERI) for idx, message in enumerate(self.rail_message_feed.list_messages()): self.cgrid_next.put( drop=idx, rest=0, s=message, cpair=solent_cpair('white')) # for drop in range(self.track_prime_console.height): for rest in range(self.track_prime_console.width): (old_c, old_cpair) = self.cgrid_last.get( drop=drop, rest=rest) (c, cpair) = self.cgrid_next.get( drop=drop, rest=rest) if c == old_c and cpair == old_cpair: continue self.nearcast.term_write( drop=drop, rest=rest, s=c, cpair=cpair) # self.cgrid_last.blit( src_cgrid=self.cgrid_next)
class RailMessageFeed: ''' This accepts text messages, and then renders them to a cgrid. Imagine text as it is coming out of an old-fashioned printer, on a feed of paper. There are two general use-cases for retrieving data from this: * You can call list_messages, and get back a list of current messages * You can call get_cgrid, and it will populate your supplied grid ''' def __init__(self): pass def zero(self, rail_h, height, width, cpair_new, cpair_old): self.rail_h = rail_h self.height = height self.width = width self.cpair_new = cpair_new self.cpair_old = cpair_old # self.cgrid = Cgrid( height=height, width=width) self.q_lines = deque() def clear(self): while self.q_lines: self.scroll() def accept(self, message, turn): nail = 0 peri = nail+self.width while len(message) > peri: self._write( text=message[nail:peri], turn=turn) nail = peri peri = peri + self.width self._write( text=message[nail:peri], turn=turn) def scroll(self): self.q_lines.popleft() def scroll_past(self, turn): while self.q_lines: first_pair = self.q_lines[0] message_turn = first_pair[1] if message_turn <= turn: self.scroll() else: break def get_height(self): return len(self.q_lines) def list_messages(self): sb = [] for (message, turn) in self.q_lines: sb.append(message) return sb def get_cgrid(self, cgrid, nail, peri, turn): self.cgrid.clear() for idx, (line, mturn) in enumerate(self.q_lines): if mturn == turn: cpair = self.cpair_new else: cpair = self.cpair_old self.cgrid.put( drop=idx, rest=0, s=line, cpair=cpair) cgrid.blit( self.cgrid, nail=nail, peri=peri) def _write(self, text, turn): self.q_lines.append( (text, turn) ) while len(self.q_lines) > self.height: self.q_lines.popleft()
class RailRoguebox: def __init__(self): pass def zero(self, rail_h, engine, grid_height, grid_width, cb_ready_alert, cb_grid_alert, cb_mail_alert, cb_over_alert): self.rail_h = rail_h self.engine = engine self.grid_height = grid_height self.grid_width = grid_width self.cb_grid_alert = cb_grid_alert self.cb_mail_alert = cb_mail_alert self.cb_ready_alert = cb_ready_alert self.cb_over_alert = cb_over_alert self.turn = 0 self.b_game_alive = False # # coordinate pools self.cpool_spare = None self.cpool_wall = None self.cpool_player = None self.cpool_weed = None self.supported_directives = None self._init_supported_directives() self.cgrid = Cgrid( height=grid_height, width=grid_width) self.q_mail_messages = deque() self.orb = None self._init_orb() self.bridge = self.orb.init_autobridge() self.bridge.nc_init( grid_height=self.grid_height, grid_width=self.grid_width) def _init_supported_directives(self): self.supported_directives = [ globals()[key] for key in globals().keys() if key.startswith('DIRECTIVE_')] def _init_orb(self): i_nearcast = ''' i message h i field h message init field grid_height field grid_width ''' self.orb = self.engine.init_orb( i_nearcast=i_nearcast) self.orb.set_spin_h('swamp_monster_orb') #self.orb.add_log_snoop() def get_supported_directives(self): ''' Returns list of instances of solent.rogue.directive representing the directives that this instance cares about. The reason for this design is that it allows the container to handle input configuration. For example, if you want the number 7 to mean north-east, you will want to be able to configure that. And it would be a distraction from the game engine itself. ''' return self.supported_directives[:] def new_game(self): self.turn = 0 self.b_game_alive = True self._zero_coord_pools() self._create_board() self.cb_ready_alert() self._announce("You are in the garden. Eliminate those evil weeds!") self._announce("[Press ? for help]") def _zero_coord_pools(self): self.cpool_spare = [] self.cpool_wall = [] self.cpool_player = [] self.cpool_weed = [] room_width = 10 room_height = 10 nail_drop = int( (self.grid_height / 2) - (room_height / 2) ) nail_rest = int( (self.grid_width / 2) - (room_width / 2) ) peri_drop = nail_drop + room_width + 1 peri_rest = nail_rest + room_height + 1 for drop in range(nail_drop, peri_drop): for rest in range(nail_rest, peri_rest): self.cpool_spare.append( (drop, rest) ) def get_turn(self): return self.turn def get_cgrid(self, cgrid, nail, peri): self._render_cgrid() cgrid.blit( src_cgrid=self.cgrid, nail=nail, peri=peri) def retrieve_mail(self): l = [] while self.q_mail_messages: l.append(self.q_mail_messages.popleft()) return l def directive(self, directive_h): if directive_h == 'help': for line in HELP.split('\n'): self._announce(line) return if False == self.b_game_alive: return else: self.turn += 1 player_spot = self.cpool_player[0] target_spot = list(player_spot) if directive_h in 'nw|ww|sw'.split('|'): target_spot[1] -= 1 if directive_h in 'ne|ee|se'.split('|'): target_spot[1] += 1 if directive_h in 'nw|nn|ne'.split('|'): target_spot[0] -= 1 if directive_h in 'sw|ss|se'.split('|'): target_spot[0] += 1 self._player_move( player_spot=player_spot, target_spot=tuple(target_spot)) if 0 == len(self.cpool_weed): self._announce('You win!') self._game_over() def _player_move(self, player_spot, target_spot): if target_spot in self.cpool_spare: self.cpool_player.remove(player_spot) self.cpool_spare.append(player_spot) self.cpool_spare.remove(target_spot) self.cpool_player.append(target_spot) self.cb_grid_alert() return elif target_spot in self.cpool_weed: self._announce( message='You slash angrily at the weed!') self.cpool_player.remove(player_spot) self.cpool_spare.append(player_spot) self.cpool_weed.remove(target_spot) self.cpool_player.append(target_spot) self.cb_grid_alert() def _announce(self, message): self.q_mail_messages.append(message) self.cb_mail_alert() def _game_over(self): self.b_game_alive = False self.cb_over_alert() def _create_board(self): coord = ( int(self.grid_height/2), int(self.grid_width/2) ) self.cpool_spare.remove(coord) self.cpool_player.append(coord) # # create the walls room_width = 10 room_height = 10 nail_drop = int( (self.grid_height / 2) - (room_height / 2) ) nail_rest = int( (self.grid_width / 2) - (room_width / 2) ) peri_drop = nail_drop + room_width + 1 peri_rest = nail_rest + room_height + 1 # horizontal walls, including corners for rest in range(nail_rest, peri_rest): coord = (nail_drop, rest) self.cpool_spare.remove(coord) self.cpool_wall.append(coord) coord = (nail_drop+room_height, rest) self.cpool_spare.remove(coord) self.cpool_wall.append(coord) # vertical walls, except corners for drop in range(nail_drop+1, peri_drop-1): coord = (drop, nail_rest) self.cpool_spare.remove(coord) self.cpool_wall.append(coord) coord = (drop, nail_rest+room_width) self.cpool_spare.remove(coord) self.cpool_wall.append(coord) # # place weeds # (we need at least one.) while not self.cpool_weed: for coord in self.cpool_spare: (drop, rest) = coord if drop < 8 or drop > 16: continue if rest < 34 or rest > 46: continue if random.random() > 0.98: self.cpool_spare.remove(coord) self.cpool_weed.append(coord) def _render_cgrid(self): self.cgrid.clear() for coord in self.cpool_spare: (drop, rest) = coord (c, cpair) = (' ', solent_cpair('teal')) self.cgrid.put( drop=drop, rest=rest, s=c, cpair=cpair) for coord in self.cpool_wall: (drop, rest) = coord (c, cpair) = PAIR_WALL self.cgrid.put( drop=drop, rest=rest, s=c, cpair=cpair) for coord in self.cpool_player: (drop, rest) = coord (c, cpair) = PAIR_PLAYER if not self.b_game_alive: cpair = solent_cpair('blue') self.cgrid.put( drop=drop, rest=rest, s=c, cpair=cpair) for coord in self.cpool_weed: (drop, rest) = coord (c, cpair) = PAIR_WEED self.cgrid.put( drop=drop, rest=rest, s=c, cpair=cpair)
class SpinSelectionUi: def __init__(self, spin_h, engine, console_type, cb_selui_keycode, cb_selui_lselect): self.spin_h = spin_h self.engine = engine self.console_type = console_type self.cb_selui_keycode = cb_selui_keycode self.cb_selui_lselect = cb_selui_lselect # self.cs_selui_keycode = CsSeluiKeycode() self.cs_selui_lselect = CsSeluiLselect() # self.width = None self.height = None self.cgrid = None self.mode = MODE_NONE self.console = None self.select_cursor_on = True self.select_cursor_t100 = time.time() * 100 self.select_drop = 0 self.select_rest = 0 self.select_cgrid = None # self.lmousedown_coords = None # def eng_turn(self, activity): if self.mode == MODE_NONE: return # keycode = self.console.async_get_keycode() if keycode: activity.mark(l=self, s='received keystroke %s' % keycode) if keycode == solent_keycode('lmousedown'): self.lmousedown_coords = self.console.get_last_lmousedown() elif keycode == solent_keycode('lmouseup'): self.lmouseup_coords = self.console.get_last_lmouseup() elif keycode == solent_keycode('rmousedown'): self.rmousedown_coords = self.console.get_last_rmousedown() elif keycode == solent_keycode('rmouseup'): self.rmouseup_coords = self.console.get_last_rmouseup() self.accept_key(keycode=keycode) # self.refresh_console() def eng_close(self): if None != self.console: self.console.close() # def open_console(self, width, height): self.width = width self.height = height self.cgrid = Cgrid(width=width, height=height) self.select_cgrid = Cgrid(width=width, height=height) self.console = Console(console_type=self.console_type, width=width, height=height) self.mode = MODE_STANDARD def close_console(self): if not self.console: return self.console.close() self.mode = MODE_NONE self.select_drop = 0 self.select_rest = 0 def to_mode_standard(self): if None == self.console: raise Exception("No console open") self.mode = MODE_STANDARD def to_mode_select(self): if None == self.console: raise Exception("No console open") self.mode = MODE_SELECT self.select_cursor_on = True self.select_cursor_t100 = time.time() * 100 def scroll(self): self.cgrid.scroll() def clear(self): self.cgrid.clear() def write(self, drop, rest, s, cpair): self.cgrid.put(drop=drop, rest=rest, s=s, cpair=cpair) def accept_key(self, keycode): ''' By making this an exposed command, we can allow the console to be used as a display with input coming from elsewhere. ''' if self.mode == MODE_SELECT: if keycode == solent_keycode('esc'): self.to_mode_standard() elif keycode in (solent_keycode('newline'), solent_keycode('s')): (c, cpair) = self.cgrid.get(drop=self.select_drop, rest=self.select_rest) self._call_selui_lselect(drop=self.select_drop, rest=self.select_rest, c=c, cpair=cpair) self.to_mode_standard() else: # we let the user navigate the cursor using arrow keys, vi # keys, gollop keys. b_moved = False # standard navigation if keycode in (solent_keycode('q'), solent_keycode('a'), solent_keycode('z'), solent_keycode('y'), solent_keycode('h'), solent_keycode('b')): if self.select_rest > 0: self.select_rest -= 1 b_moved = True if keycode in (solent_keycode('e'), solent_keycode('d'), solent_keycode('c'), solent_keycode('u'), solent_keycode('l'), solent_keycode('n')): if self.select_rest < self.width - 1: self.select_rest += 1 b_moved = True if keycode in (solent_keycode('q'), solent_keycode('w'), solent_keycode('e'), solent_keycode('y'), solent_keycode('k'), solent_keycode('u')): if self.select_drop > 0: self.select_drop -= 1 b_moved = True if keycode in (solent_keycode('z'), solent_keycode('x'), solent_keycode('c'), solent_keycode('b'), solent_keycode('j'), solent_keycode('n')): if self.select_drop < self.height - 1: self.select_drop += 1 b_moved = True # shift navigation if keycode in (solent_keycode('Q'), solent_keycode('A'), solent_keycode('Z'), solent_keycode('Y'), solent_keycode('H'), solent_keycode('B')): self.select_rest = 0 b_moved = True if keycode in (solent_keycode('E'), solent_keycode('D'), solent_keycode('C'), solent_keycode('U'), solent_keycode('L'), solent_keycode('N')): self.select_rest = self.width - 1 b_moved = True if keycode in (solent_keycode('Q'), solent_keycode('W'), solent_keycode('E'), solent_keycode('Y'), solent_keycode('K'), solent_keycode('U')): self.select_drop = 0 b_moved = True if keycode in (solent_keycode('Z'), solent_keycode('X'), solent_keycode('C'), solent_keycode('B'), solent_keycode('J'), solent_keycode('N')): self.select_drop = self.height - 1 b_moved = True # if b_moved: self.select_cursor_on = True self.select_cursor_t100 = time.time() * 100 elif self.mode == MODE_STANDARD: if keycode == solent_keycode('esc'): self.to_mode_select() else: if keycode == solent_keycode('lmouseup'): # We check to see that the coords were the same as # when the mouse was depressed. If they weren't, this # usually implies the user has rethought their # decision, and we abort. From memory, this is how FTL # works, and it's super-useful. (ddrop, drest) = self.lmousedown_coords (udrop, urest) = self.lmouseup_coords if (ddrop, drest) == (udrop, urest): (c, cpair) = self.cgrid.get(drop=udrop, rest=urest) self._call_selui_lselect(drop=udrop, rest=urest, c=c, cpair=cpair) elif keycode in MOUSE_EVENTS: pass else: # we pass the keystroke back in a callback self._call_selui_keycode(keycode=keycode) def refresh_console(self): if self.mode == MODE_NONE: pass elif self.mode == MODE_SELECT: # cursor flipping t100 = time.time() * 100 if t100 - self.select_cursor_t100 > 53: self.select_cursor_t100 = t100 if self.select_cursor_on: self.select_cursor_on = False else: self.select_cursor_on = True # cursor display self.select_cgrid.blit(src_cgrid=self.cgrid) if self.select_cursor_on: self.select_cgrid.put(drop=self.select_drop, rest=self.select_rest, s='@', cpair=solent_cpair('red')) self.console.screen_update(cgrid=self.select_cgrid) elif self.mode == MODE_STANDARD: self.console.screen_update(cgrid=self.cgrid) # def _call_selui_keycode(self, keycode): self.cs_selui_keycode.keycode = keycode self.cb_selui_keycode(cs_selui_keycode=self.cs_selui_keycode) def _call_selui_lselect(self, drop, rest, c, cpair): self.cs_selui_lselect.drop = drop self.cs_selui_lselect.rest = rest self.cs_selui_lselect.c = c self.cs_selui_lselect.cpair = cpair self.cb_selui_lselect(cs_selui_lselect=self.cs_selui_lselect)
class RailMessageFeed: def __init__(self): pass def zero(self, rail_h, height, width, cpair_new, cpair_old): self.rail_h = rail_h self.height = height self.width = width self.cpair_new = cpair_new self.cpair_old = cpair_old # self.cgrid = Cgrid( height=height, width=width) self.q_lines = deque() def clear(self): while self.q_lines: self.scroll() def accept(self, message, turn): nail = 0 peri = nail+self.width while len(message) > peri: self._write( text=message[nail:peri], turn=turn) nail = peri peri = peri + self.width self._write( text=message[nail:peri], turn=turn) def scroll(self): self.q_lines.popleft() def scroll_past(self, turn): while self.q_lines: first_pair = self.q_lines[0] message_turn = first_pair[1] if message_turn <= turn: self.scroll() else: break def get_height(self): return len(self.q_lines) def list_messages(self): sb = [] for (message, turn) in self.q_lines: sb.append(message) return sb def get_cgrid(self, cgrid, nail, peri, turn): self.cgrid.clear() for idx, (line, mturn) in enumerate(self.q_lines): if mturn == turn: cpair = self.cpair_new else: cpair = self.cpair_old self.cgrid.put( drop=idx, rest=0, s=line, cpair=cpair) cgrid.blit( self.cgrid, nail=nail, peri=peri) def _write(self, text, turn): self.q_lines.append( (text, turn) ) while len(self.q_lines) > self.height: self.q_lines.popleft()