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)
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
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 should_create_two_simple_grids(): grid_a = Cgrid(5, 5) cgrid_populate(grid_a, '-') cgrid_console_print(grid_a) # grid_b = Cgrid(3, 3) cgrid_populate(grid_b, '|') cgrid_console_print(grid_b) # return True
def __init__(self, width, height, font): self.width = width self.height = height self.font = font # self.internal_cgrid = Cgrid(width=width, height=height) (self.cwidth, self.cheight) = font.size('@') # dim = (width * self.cwidth, height * self.cheight) self.screen = pygame.display.set_mode(dim)
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 __init__(self, console, cgrid, title): self.console = console self.cgrid = cgrid self.title = title # self.b_menu_active = True self.game = None # # Later on we could use something like pyfiglet for this. Better would # be a single distinct font, similar to what Gollop did with rebelstar. self.title_cgrid = Cgrid(width=len(self.title), height=1) self.title_cgrid.put(drop=0, rest=0, s=self.title, cpair=CPAIR_TITLE) # self.menu = menu_new() self.menu_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 main(): if '--curses' in sys.argv: console_type = 'curses' elif '--pygame' in sys.argv: console_type = 'pygame' else: print('ERROR: specify either --curses or --pygame') sys.exit(1) # console = None try: console = Console(console_type=console_type, width=C_GAME_WIDTH, height=C_GAME_HEIGHT) # cgrid = Cgrid(width=console.width, height=console.height) husk = Husk(console=console, cgrid=cgrid, title=TITLE) # # event loop husk.event_loop() except SolentQuitException: pass except: traceback.print_exc() finally: if None != console: console.close()
def rogue_interaction_new(console, cursor): cgrid = Cgrid(width=console.width, height=console.height) logbook = logbook_new(capacity=100) ob = RogueInteraction(console=console, cursor=cursor, cgrid=cgrid, logbook=logbook) return ob
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)
class GridDisplay(object): def __init__(self, width, height, font): self.width = width self.height = height self.font = font # self.internal_cgrid = Cgrid(width=width, height=height) (self.cwidth, self.cheight) = font.size('@') # dim = (width * self.cwidth, height * self.cheight) self.screen = pygame.display.set_mode(dim) def update(self, cgrid): cur_dim = (self.internal_cgrid.width, self.internal_cgrid.height) new_dim = (cgrid.width, cgrid.height) if cur_dim != new_dim: self.internal_cgrid.set_dimensions(width=cgrid.width, height=cgrid.height) updates = [] o_spots = self.internal_cgrid.spots n_spots = cgrid.spots for idx, (o_spot, n_spot) in enumerate(zip(o_spots, n_spots)): if not o_spot.compare(n_spot): updates.append(idx) o_spot.mimic(n_spot) for idx in updates: spot = self.internal_cgrid.spots[idx] (font_fg, font_bg) = MAP_CONST_COLOURS_TO_CPAIR[spot.cpair] label = self.font.render(spot.c, 1, font_fg, font_bg) # drop_pixels = self.cwidth * int(idx % self.internal_cgrid.width) rest_pixels = self.cheight * int(idx / self.internal_cgrid.width) self.screen.blit(label, (drop_pixels, rest_pixels)) pygame.display.flip() def coords_from_mousepos(self, xpos, ypos): ''' From the supplied pixel dimensions, return the drop and rest of the selected grid reference. ''' rest = int(xpos / self.cwidth) drop = int(ypos / self.cheight) return (drop, rest)
def event_loop(console): cgrid = Cgrid( width=console.width, height=console.height) cgrid.put( drop=console.height-3, rest=1, s='(Escape to quit)', cpair=solent_cpair('white')) for (idx, (cpair, name)) in enumerate(solent_cpair_pairs()): cgrid.put( drop=(5+int(idx/4)), rest=(2+int(18*(idx%4))), s='%s %s'%(name, cpair), cpair=cpair) console.screen_update( cgrid=cgrid) # t = 0 while True: keycode = console.async_get_keycode() if keycode != None: if keycode == solent_keycode('esc'): raise SolentQuitException() cgrid.put( drop=3, rest=1, s='key %s (%s) '%(hex(keycode), chr(keycode)), cpair=solent_cpair('red')) else: time.sleep(0.05) cgrid.put( drop=1, rest=1, s='loop counter: %s'%(t), cpair=solent_cpair('green')) console.screen_update( cgrid=cgrid) t += 1
def zero(self, zero_h, cb_grid_console_splat, cb_grid_console_kevent, cb_grid_console_mevent, cb_grid_console_closed, engine, width, height): self.zero_h = zero_h self.cb_grid_console_splat = cb_grid_console_splat self.cb_grid_console_kevent = cb_grid_console_kevent self.cb_grid_console_mevent = cb_grid_console_mevent self.cb_grid_console_closed = cb_grid_console_closed self.engine = engine self.width = width self.height = height # if self.width > 200 or self.height > 100: # Note that width and height are by character, not by screen # dimension. log("Warning. Have seen crashes as screen dimensions become vast.") log('width %s %s' % (self.width, type(self.width))) log('height %s %s' % (self.height, type(self.height))) # self.cgrid = Cgrid(width=width, height=height) # # Emphasis: do not use named parameters for this, or the variable # holding the logger will get garbage-collected. You must directly # refer to cc_log. # xxx rename api to c_api self.api.set_cc_log(cc_log) # log('impl_grid_console w:%s h:%s' % (self.width, self.height)) self.api.create_screen(self.width, self.height) self.api.process_windows_events() # self.spin_windows_events = self.engine.init_spin( construct=SpinWindowsEvents, cb_windows_event_quit=self.cb_windows_event_quit, cb_windows_event_kevent=self.cb_windows_event_kevent, api=self.api)
def _allocate_menu_cgrid(self): ''' xxx it's garbagey to repreatedly recreate this. create an issue. better solution: plot the menu directly to the main cgrid, rather than having separate cgrids for title and menu. ''' lines = self.menu.get_lines() longest_line = 0 for l in lines: longest_line = max([longest_line, len(l)]) # # prepare the menu border self.menu_cgrid = Cgrid(width=longest_line + 4, height=len(lines) + 2) horiz = ' ' * (longest_line + 4) menu_border_height = len(lines) + 2 for idx in range(menu_border_height): if idx in (0, menu_border_height - 1): self.menu_cgrid.put(drop=idx, rest=0, s=horiz, cpair=CPAIR_MENU_BORDER) else: line = lines[idx - 1] self.menu_cgrid.put(drop=idx, rest=0, s=' ', cpair=CPAIR_MENU_BORDER) self.menu_cgrid.put(drop=idx, rest=1, s=' %s%s ' % (line, ' ' * (longest_line - len(line))), cpair=CPAIR_MENU_TEXT) self.menu_cgrid.put(drop=idx, rest=longest_line + 3, s=' ', cpair=CPAIR_MENU_BORDER)
def create(width, height): global CONSOLE if None != CONSOLE: raise Exception( 'curses console is singleton. (cannot run more than one)') cgrid = Cgrid(width=width, height=height) # # Weird: it looks like you need to declare the keystroke source before # you do screen init. These kind of bizarre ordering problems are the # reason that it's good to have this stuff wrapped up in a library: # solve the nasty problem, and then facade things so your user doesn't # have to think about it. keystream = keystream_new(cb_async_get_keycode=curses_async_get_keycode, cb_block_get_keycode=curses_block_get_keycode) grid_display = GridDisplay(internal_cgrid=cgrid) # # Emphasis: see note about regarding keystream: ordering is significant. screen_curses_init() # def on_close(): screen_curses_exit() # global CONSOLE CONSOLE = None CONSOLE = iconsole_new(keystream=keystream, grid_display=grid_display, width=width, height=height, cb_last_lmouseup=lambda: None, cb_last_lmousedown=lambda: None, cb_last_rmouseup=lambda: None, cb_last_rmousedown=lambda: None, cb_close=on_close) return CONSOLE
def should_copy_fine_despite_us_not_supplying_a_nail_param_to_blit(): grid_a = Cgrid(5, 5) zyx = [chr(i + 65) for i in range(26)] zyx.reverse() for i in range(25): l = zyx[i] drop = i / 5 rest = i % 5 grid_a.put(drop=drop, rest=rest, s=l.lower(), cpair=DEFAULT_CPAIR) # grid_b = Cgrid(3, 3) grid_b.put(0, 0, '0', DEFAULT_CPAIR) grid_b.put(0, 1, '1', DEFAULT_CPAIR) grid_b.put(0, 2, '2', DEFAULT_CPAIR) grid_b.put(1, 0, '3', DEFAULT_CPAIR) grid_b.put(1, 1, '4', DEFAULT_CPAIR) grid_b.put(1, 2, '5', DEFAULT_CPAIR) grid_b.put(2, 0, '6', DEFAULT_CPAIR) grid_b.put(2, 1, '7', DEFAULT_CPAIR) grid_b.put(2, 2, '8', DEFAULT_CPAIR) # grid_a.blit(src_cgrid=grid_b) cgrid_console_print(grid_a) # return True
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)
def should_copy_one_grid_onto_another(): grid_a = Cgrid(5, 5) cgrid_populate(grid_a, '*') # grid_b = Cgrid(3, 3) grid_b.put(0, 0, 'a', DEFAULT_CPAIR) grid_b.put(0, 1, 'b', DEFAULT_CPAIR) grid_b.put(0, 2, 'c', DEFAULT_CPAIR) grid_b.put(1, 0, 'd', DEFAULT_CPAIR) grid_b.put(1, 1, 'e', DEFAULT_CPAIR) grid_b.put(1, 2, 'f', DEFAULT_CPAIR) grid_b.put(2, 0, 'g', DEFAULT_CPAIR) grid_b.put(2, 1, 'h', DEFAULT_CPAIR) grid_b.put(2, 2, 'i', DEFAULT_CPAIR) # grid_a.blit(grid_b) cgrid_console_print(grid_a) # return True
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()
def should_do_a_truncated_copy_on_the_right_side(): grid_a = Cgrid(5, 5) zyx = [chr(i + 65) for i in range(26)] zyx.reverse() for i in range(25): l = zyx[i] drop = int(i / 5) rest = i % 5 grid_a.put(drop=drop, rest=rest, s=l.lower(), cpair=DEFAULT_CPAIR) # grid_b = Cgrid(3, 3) grid_b.put(0, 0, '0', DEFAULT_CPAIR) grid_b.put(0, 1, '1', DEFAULT_CPAIR) grid_b.put(0, 2, '2', DEFAULT_CPAIR) grid_b.put(1, 0, '3', DEFAULT_CPAIR) grid_b.put(1, 1, '4', DEFAULT_CPAIR) grid_b.put(1, 2, '5', DEFAULT_CPAIR) grid_b.put(2, 0, '6', DEFAULT_CPAIR) grid_b.put(2, 1, '7', DEFAULT_CPAIR) grid_b.put(2, 2, '8', DEFAULT_CPAIR) # grid_a.blit(src_cgrid=grid_b, nail=(1, 3)) cgrid_console_print(grid_a) # return True
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)
def husk_new(console): cgrid = Cgrid(width=console.width, height=console.height) ob = Husk(console=console, cgrid=cgrid, title=TITLE) return ob
class ImplGridConsole: def __init__(self): self.form = FormGridConsole(self) # clib = load_clib(self) # self.api = Ns() self.api.set_cc_log = init_ext_fn(None, clib.set_cc_log, [CC_LOG_T]) self.api.create_screen = init_ext_fn(None, clib.create_screen, [c_int, c_int]) self.api.process_windows_events = init_ext_fn( c_int, clib.process_windows_events, []) self.api.get_next_event = init_ext_fn(None, clib.get_next_event, [c_uint64_p]) self.api.set = init_ext_fn(None, clib.set, [c_int, c_int, c_int, c_int]) self.api.redraw = init_ext_fn(None, clib.redraw, []) self.api.close = init_ext_fn(None, clib.close, []) def zero(self, zero_h, cb_grid_console_splat, cb_grid_console_kevent, cb_grid_console_mevent, cb_grid_console_closed, engine, width, height): self.zero_h = zero_h self.cb_grid_console_splat = cb_grid_console_splat self.cb_grid_console_kevent = cb_grid_console_kevent self.cb_grid_console_mevent = cb_grid_console_mevent self.cb_grid_console_closed = cb_grid_console_closed self.engine = engine self.width = width self.height = height # if self.width > 200 or self.height > 100: # Note that width and height are by character, not by screen # dimension. log("Warning. Have seen crashes as screen dimensions become vast.") log('width %s %s' % (self.width, type(self.width))) log('height %s %s' % (self.height, type(self.height))) # self.cgrid = Cgrid(width=width, height=height) # # Emphasis: do not use named parameters for this, or the variable # holding the logger will get garbage-collected. You must directly # refer to cc_log. # xxx rename api to c_api self.api.set_cc_log(cc_log) # log('impl_grid_console w:%s h:%s' % (self.width, self.height)) self.api.create_screen(self.width, self.height) self.api.process_windows_events() # self.spin_windows_events = self.engine.init_spin( construct=SpinWindowsEvents, cb_windows_event_quit=self.cb_windows_event_quit, cb_windows_event_kevent=self.cb_windows_event_kevent, api=self.api) def cb_windows_event_quit(self, cs_windows_event_quit): zero_h = cs_windows_event_quit.zero_h # raise SolentQuitException() def cb_windows_event_kevent(self, cs_windows_event_kevent): zero_h = cs_windows_event_kevent.zero_h k = cs_windows_event_kevent.k # # xxx we will need to do some translation here from windows-style # keycodes to solent style. for the moment I am just passing back the # raw values. keycode = k self.form.call_grid_console_kevent(zero_h=self.zero_h, keycode=keycode) # def send(self, cgrid): cur_dim = (self.cgrid.width, self.cgrid.height) new_dim = (cgrid.width, cgrid.height) if cur_dim != new_dim: self.cgrid.set_dimensions(width=cgrid.width, height=cgrid.height) updates = [] o_spots = self.cgrid.spots n_spots = cgrid.spots for cgrid_idx, (o_spot, n_spot) in enumerate(zip(o_spots, n_spots)): if not o_spot.compare(n_spot): updates.append(cgrid_idx) o_spot.mimic(n_spot) log('send') for cgrid_idx in updates: spot = self.cgrid.spots[cgrid_idx] # drop = int(cgrid_idx / self.cgrid.width) rest = int(cgrid_idx % self.cgrid.width) c = ord(spot.c) o = 0 # xxx log('update %s %s %s %s' % (drop, rest, c, o)) self.api.set(drop, rest, c, o) self.api.redraw() def close(self): log('close') return self.api.close()
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()
class Husk(object): def __init__(self, console, cgrid, title): self.console = console self.cgrid = cgrid self.title = title # self.b_menu_active = True self.game = None # # Later on we could use something like pyfiglet for this. Better would # be a single distinct font, similar to what Gollop did with rebelstar. self.title_cgrid = Cgrid(width=len(self.title), height=1) self.title_cgrid.put(drop=0, rest=0, s=self.title, cpair=CPAIR_TITLE) # self.menu = menu_new() self.menu_cgrid = None # def _generate_menu_content(self): def mi_new_game(): print('xxx new game') self.game = game_new(console=self.console) self.b_menu_active = False def mi_load_game(): print('xxx __mi_load_game') def mi_continue_game(): print('xxx continue game') self.b_menu_active = False def mi_save_game(): print('xxx __mi_save_game') def mi_quit(): raise SolentQuitException() self.menu.clear() if None != self.game: self.menu.add('c', 'continue', mi_continue_game) self.menu.add('s', 'save', mi_save_game) self.menu.add('l', 'load', mi_load_game) self.menu.add('n', 'new', mi_new_game) self.menu.add('q', 'quit', mi_quit) def _allocate_menu_cgrid(self): ''' xxx it's garbagey to repreatedly recreate this. create an issue. better solution: plot the menu directly to the main cgrid, rather than having separate cgrids for title and menu. ''' lines = self.menu.get_lines() longest_line = 0 for l in lines: longest_line = max([longest_line, len(l)]) # # prepare the menu border self.menu_cgrid = Cgrid(width=longest_line + 4, height=len(lines) + 2) horiz = ' ' * (longest_line + 4) menu_border_height = len(lines) + 2 for idx in range(menu_border_height): if idx in (0, menu_border_height - 1): self.menu_cgrid.put(drop=idx, rest=0, s=horiz, cpair=CPAIR_MENU_BORDER) else: line = lines[idx - 1] self.menu_cgrid.put(drop=idx, rest=0, s=' ', cpair=CPAIR_MENU_BORDER) self.menu_cgrid.put(drop=idx, rest=1, s=' %s%s ' % (line, ' ' * (longest_line - len(line))), cpair=CPAIR_MENU_TEXT) self.menu_cgrid.put(drop=idx, rest=longest_line + 3, s=' ', cpair=CPAIR_MENU_BORDER) def _render_title(self): self.cgrid.blit(src_cgrid=self.title_cgrid, nail=(0, 0)) def _render_menu(self): menu_drop = int((self.cgrid.height / 2) - (self.menu_cgrid.height / 2)) menu_rest = int((self.cgrid.width / 2) - (self.menu_cgrid.width / 2)) nail = (menu_drop, menu_rest) self.cgrid.blit(src_cgrid=self.menu_cgrid, nail=nail) def _render(self): self._generate_menu_content() self._allocate_menu_cgrid() self._render_title() self._render_menu() self.console.screen_update(cgrid=self.cgrid) # def event_loop(self): self._render() while True: # # Defensive if self.game == None: self.b_menu_active = True # # Input if self.b_menu_active: keycode = self.console.block_get_keycode() elif self.game != None and self.game.player_mind.is_blocking(): keycode = self.console.block_get_keycode() else: keycode = self.console.async_get_keycode() b_key = True if keycode == None: b_key = False # # Menu if self.b_menu_active: if b_key: if keycode == solent_keycode('tab') and self.game != None: self.b_menu_active = False keycode = None elif self.menu.has_key(chr(keycode)): fn = self.menu.get_callback(chr(keycode)) fn() keycode = None else: if b_key: if keycode == solent_keycode('tab'): self.b_menu_active = True keycode = None self._render() continue else: self.game.accept_key(key=chr(keycode)) keycode = None # # Update display. if self.b_menu_active: self._render() else: activity = self.game.turn() if activity: time.sleep(0.05) if not activity: time.sleep(0.1)