class TextElement(Sprite): def __init__(self, text, sx, sy, w=-1, h=-1): Sprite.__init__(self) self.sx = sx self.sy = sy self.w = w self.h = h self.setText(text) def setText(self, text): self.text = text dims = getSurfaceDimsFromText(text, TileService.size, self.w, self.h) print('text dims', dims) print('sx', self.sx, 'sy', self.sy) self.image = Surface(dims) self.rect = ( self.sx, self.sy, dims[0], dims[1] ) blits = [] x = 0 y = 0 for char in self.text: blits.append((TileService.getTileByChar(char), (x, y))) x += TileService.size self.image.blits(blit_sequence=blits)
def update_power(self, screen: pygame.Surface, val): self.power_viewer.fill(col_screen) self.power_text = self.font_param.render(str(val), True, col_text_white, col_screen) screen.blits(((self.power_viewer, self.power_viewer_rect), (self.power_text, self.power_text_rect)), 0) # redraw power bar fill pygame.draw.rect(screen, col_screen, (self.fill_x, self.fill_y, self.fill_w, self.fill_h)) self.power_bar_fill_rect = pygame.draw.rect(screen, col_power, (self.fill_x, self.fill_y, self.fill_w * (val / 100), self.fill_h))
def scroll_text(self, text) -> None: """Fills the main screen with black and rolls up the passed text. font and size are hardcoded NOTE: for now when passing a long text, lines shouldn't be longer than `43` characters or the text will be cut. this is due to the fact that I wanted the textbox to be only 2/3 of the window's width Parameters ---------- text -> text to be scrolled up Tests ----- - passing a very long text - passing a text with very long lines - passing text with escapeable chars """ splitted = text.splitlines() self.screen.fill(BLACK) complete_surface = Surface((WIDTH * 2 // 3, 30 * len(splitted))) text_lines = [] for index, line in enumerate(splitted): line = line.strip() text_surface, text_surface_rect = self.draw_text( line, 32, (WHITE, RED), x=0, y=32 * index, surface=complete_surface) text_lines.append((text_surface, text_surface_rect)) complete_surface_rect = complete_surface.get_rect() complete_surface_rect.topleft = (WIDTH // 6, HEIGHT) waiting = True # while the bot of the text surface has not reached the top of screen while waiting and complete_surface_rect.bottom > 0: # scroll text up for event in pg.event.get(): if event.type == pg.QUIT: waiting = False self.running = False if event.type == pg.KEYUP: waiting = False self.clock.tick(60) complete_surface_rect.y -= 1 complete_surface.blits(text_lines, False) self.screen.blit(complete_surface, complete_surface_rect) pg.display.flip()
def __init__(self, scene, location: Tuple[int, int], font_path, word, size: int, frame_length: int = 60): self.frame_length = frame_length self.frame_count = 0 self.angle = 0.0 self.zoom = 1.0 self.sound = pygame.mixer.Sound(str(Paths.sfx / "explosion.ogg")) self.sound.play() explosion_sets = ( ("explosion-blue.png", Colors.orange), ("explosion-orange.png", Colors.blue), ("explosion-yellow.png", Colors.red), ("explosion-red.png", Colors.yellow) ) filename, color = random.choice(explosion_sets) explosion_image: Surface = pygame.image.load(str(Paths.effects / filename)) explosion_font_size: int = 26 - (len(word) - 5) explosion_font: Font = Font(str(font_path), explosion_font_size) explosion_text: Surface = explosion_font.render(word, True, color) explosion_image = pygame.transform.smoothscale(explosion_image, (size, size)) # Turn around after x frames surface = Surface((size, size)) surface.fill(Colors.black) surface.set_colorkey(Colors.black) surface.blits( ( (explosion_image, (0, 0)), ( explosion_text, ( (explosion_image.get_width() / 2) - explosion_text.get_width() / 2, (explosion_image.get_height() / 2) - explosion_text.get_height() / 2 ) ) ) ) super().__init__(scene, location, surface)
def draw(self, surface: Surface) -> None: sprites = self.sprites() if hasattr(surface, "blits"): self.spritedict.update( zip( sprites, surface.blits((spr.image, spr.rect.move(self.camera_rect)) for spr in sprites))) else: for spr in sprites: self.spritedict[spr] = surface.blit(spr.image, spr.rect) self.lostsprites = []
def update_angle(self, screen: pygame.Surface, val): self.angle_viewer.fill(col_screen) self.angle_text = self.font_param.render(str(val), True, col_text_white, col_screen) screen.blits(((self.angle_viewer, self.angle_viewer_rect), (self.angle_text, self.angle_text_rect)), 0)
def __init__(self, screen: pygame.Surface, cpl_x, cpl_y, cpl_w, cpl_h, control_scale): control_img = pygame.image.load("../resources/images/control_panel.png").convert() self.surf = pygame.transform.scale(control_img, (cpl_w, cpl_h)) self.rect = self.surf.get_rect() self.rect.x = cpl_x self.rect.y = cpl_y screen.blit(self.surf, (cpl_x, cpl_y)) # add and draw control elements '''sec_w = cpl_w / 5 wp_x = sec_w angle_x = sec_w * 2 power_x = sec_w * 3 fire_x = sec_w * 4 ''' w1 = cpl_w / 5 rw = cpl_w - w1 sec_w = rw / 5 wp_x = w1 angle_x = w1 + sec_w power_x = w1 + sec_w * 2 fire_x = w1 + sec_w * 3 control_baseline_y = self.rect.y + self.rect.h * 0.45 # setting up a y-axis center line for controls # create surfaces wp_img = pygame.image.load("../resources/images/wp_list.png") angle_img = pygame.image.load("../resources/images/param_window.png") angle_dec_img = pygame.image.load("../resources/images/inc_button.png") angle_inc_img = pygame.image.load("../resources/images/dec_button.png") power_img = pygame.image.load("../resources/images/param_window.png") power_bar_img = pygame.image.load("../resources/images/power_bar.png") power_inc_img = pygame.image.load("../resources/images/inc_button.png") power_dec_img = pygame.image.load("../resources/images/dec_button.png") fire_img = pygame.image.load("../resources/images/param_window.png") fire_text_img = pygame.image.load("../resources/images/fire_text.png") scoreboard_frame = pygame.image.load("../resources/images/scoreboard_frame.png") # scale surfaces wp_surf = scale(wp_img, control_scale) angle_inc_surf = scale(angle_inc_img, control_scale) angle_surf = scale(angle_img, control_scale) angle_dec_surf = scale(angle_dec_img, control_scale) power_dec_surf = scale(power_dec_img, control_scale) power_surf = scale(power_img, control_scale) power_inc_surf = scale(power_inc_img, control_scale) power_bar_surf = scale(power_bar_img, control_scale) fire_surf = scale(fire_img, control_scale) fire_text_surf = scale(fire_text_img, control_scale / 1.2) scoreboard_frame_surf = scale(scoreboard_frame, control_scale) # get rects with simple names self.wp = wp_surf.get_rect() self.angle_inc = angle_inc_surf.get_rect() self.angle_sel = angle_surf.get_rect() self.angle_dec = angle_dec_surf.get_rect() self.power_dec = power_dec_surf.get_rect() self.power = power_surf.get_rect() self.power_inc = power_inc_surf.get_rect() self.power_bar = power_bar_surf.get_rect() self.fire = fire_surf.get_rect() self.fire_text = fire_text_surf.get_rect() self.scoreboard = scoreboard_frame_surf.get_rect() # set positions self.wp.center = (wp_x + angle_x) / 2, control_baseline_y self.angle_sel.center = (angle_x + power_x) / 2, control_baseline_y self.power.center = (power_x + fire_x) / 2, control_baseline_y # self.power_bar.center = (power_x + fire_x) / 2, (control_baseline_y + self.rect.bottom) / 2 # The 0.995 to place bar is irritating; need to fix self.power_bar.center = (power_x + fire_x) / 2, 0.995*(self.power.bottom + self.rect.bottom) / 2 self.angle_inc.center = ((angle_x + power_x) / 2) - self.angle_inc.w / 4 - self.angle_sel.w / 2, control_baseline_y self.angle_dec.center = ((angle_x + power_x) / 2) + self.angle_dec.w / 4 + self.angle_sel.w / 2, control_baseline_y self.power_dec.center = ((power_x + fire_x) / 2) - self.power_dec.w / 4 - self.power.w / 2, control_baseline_y self.power_inc.center = ((power_x + fire_x) / 2) + self.power_inc.w / 4 + self.power.w / 2, control_baseline_y self.fire.center = (fire_x + (fire_x + sec_w)) / 2, (self.rect.y + self.rect.h / 2) self.fire_text.center = self.fire.center self.scoreboard.center = (self.fire.centerx + sec_w, self.rect.centery) # control windows self.wp_viewer = pygame.Surface((int(self.wp.w * 0.8), int(self.wp.h * 0.6))) self.wp_viewer_rect = self.wp_viewer.get_rect() self.wp_viewer.fill(col_screen) self.wp_viewer_rect.center = self.wp.center self.angle_viewer = pygame.Surface((int(self.angle_sel.w * 0.65), int(self.angle_sel.h * 0.6))) self.angle_viewer_rect = self.angle_viewer.get_rect() self.angle_viewer.fill(col_screen) self.angle_viewer_rect.center = self.angle_sel.center self.power_viewer = pygame.Surface((int(self.power.w * 0.65), int(self.power.h * 0.6))) self.power_viewer_rect = self.power_viewer.get_rect() self.power_viewer.fill(col_screen) self.power_viewer_rect.center = self.power.center self.power_bar_viewer = pygame.Surface((int(self.power_bar.w * 0.8), int(self.power_bar.h * 0.6))) self.power_bar_viewer_rect = self.power_bar_viewer.get_rect() self.power_bar_viewer_rect.center = self.power_bar.center self.power_bar_viewer.fill(col_screen) self.score_viewer = pygame.Surface((int(self.scoreboard.w * 0.8), int(self.scoreboard.h * 0.76))) self.score_viewer_rect = self.score_viewer.get_rect() self.score_viewer_rect.center = (self.scoreboard.centerx + 1, self.scoreboard.centery + 1) self.score_viewer.fill(col_screen) # scoreboard_surf.fill(col_screen) # fill area max size and loc self.fill_w = int(self.power_bar.w * 0.78) self.fill_h = int(self.power_bar.h * 0.44) self.fill_x = int(self.power_bar_viewer_rect.x + (self.power_bar_viewer_rect.w - self.fill_w) / 2) self.fill_y = int(self.power_bar_viewer_rect.y + (self.power_bar_viewer_rect.h - self.fill_h) / 2) self.power_bar_fill_area = pygame.Rect(self.fill_x, self.fill_y, self.fill_w, self.fill_h) # max area # display text # self.font_param = pygame.font.SysFont("segoeui", 13, True) self.font_param = pygame.font.Font("../resources/fonts/expressway.ttf", 13) # self.font_heading = pygame.font.SysFont("comicsansms", 16, True) self.font_heading = pygame.font.Font("../resources/fonts/expressway.ttf", 15) self.angle_text = self.font_param.render(str(default_angle_val), True, col_text_white, col_screen) self.angle_text_rect = self.angle_text.get_rect() self.angle_text_rect.center = self.angle_viewer_rect.center self.angle_heading = self.font_heading.render("Angle", True, col_text_white) self.angle_heading_rect = self.angle_heading.get_rect() self.angle_heading_rect.center = (self.angle_viewer_rect.centerx, (self.angle_viewer_rect.top + self.rect.top) / 2) self.power_text = self.font_param.render(str(default_power_val), True, col_text_white, col_screen) self.power_text_rect = self.power_text.get_rect() self.power_text_rect.center = self.power_viewer_rect.center self.power_heading = self.font_heading.render("Power", True, col_text_white) self.power_heading_rect = self.power_heading.get_rect() self.power_heading_rect.center = (self.power_viewer_rect.centerx, (self.power_viewer_rect.top + self.rect.top) / 2) self.wp_text = self.font_param.render("Plain ammo", True, col_text_white, col_screen) self.wp_text_rect = self.wp_text.get_rect() self.wp_text_rect.center = self.wp_viewer_rect.center self.wp_heading = self.font_heading.render("Weapon", True, col_text_white) self.wp_heading_rect = self.wp_heading.get_rect() self.wp_heading_rect.center = (self.wp_viewer_rect.centerx, (self.wp_viewer_rect.top + self.rect.top) / 2) self.scoreboard_heading = self.font_param.render("Scoreboard", True, col_text_white) self.scoreboard_heading_rect = self.scoreboard_heading.get_rect() self.scoreboard_heading_rect.center = (self.score_viewer_rect.centerx, self.score_viewer_rect.top + self.score_viewer_rect.h / 9) '''self.fire_text = self.font.render("Fire", True, col_fire) self.fire_text_rect = self.fire_text.get_rect() self.fire_text_rect.center = self.fire.center''' # draw control elements screen.blits(((wp_surf, self.wp), (angle_surf, self.angle_sel), (power_surf, self.power), (power_bar_surf, self.power_bar), (fire_surf, self.fire), (angle_dec_surf, self.angle_dec), (angle_inc_surf, self.angle_inc), (power_inc_surf, self.power_inc), (power_dec_surf, self.power_dec), (self.wp_viewer, self.wp_viewer_rect), (self.angle_viewer, self.angle_viewer_rect), (self.power_viewer, self.power_viewer_rect), (self.power_bar_viewer, self.power_bar_viewer_rect), # (self.power_bar_fill, self.power_bar_fill_rect), (self.angle_text, self.angle_text_rect), (self.power_text, self.power_text_rect), (self.wp_text, self.wp_text_rect), # (self.fire_text, self.fire_text_rect) (self.angle_heading, self.angle_heading_rect), (self.power_heading, self.power_heading_rect), (self.wp_heading, self.wp_heading_rect), (fire_text_surf, self.fire_text), (scoreboard_frame_surf, self.scoreboard), (self.score_viewer, self.score_viewer_rect), (self.scoreboard_heading, self.scoreboard_heading_rect) ), 0) # fill bar at the end self.power_bar_fill_rect = pygame.draw.rect(screen, col_power, (self.fill_x, self.fill_y, int(self.fill_w * (default_power_val / 100)), self.fill_h))
class Board: squares = [] def __init__(self, size): self.size = (size, size) self.sq_size = int(self.size[1] / 8) self.surface = Surface(self.size) def within(self, pos): return pos[0] < self.size[0] and pos[1] < self.size[1] def clear(self): for row in self.squares: for sq in row: sq.fresh = False sq.hover = False def square(self, pos=None, query=None): squar = None if (pos): point = lambda i: floor(pos[i] / self.sq_size) squar = self.squares[(int)(point(1))][(int)(point(0))] if (query): match = True for k, v in query.items(): if (squar): match = v and getattr(squar, k) else: for rank in self.squares: for sq in rank: squar = sq if v and getattr(sq, k) else None if (squar): break if (squar): break squar = squar if match else None return squar def update(self): for row in self.squares: row_blits = [] for sq in row: if (not sq.fresh): sq.draw() row_blits.append((sq.surface, (sq.x, sq.y))) self.surface.blits(row_blits) return self.surface def draw(self, square=None): sq_pad = 8 font_size = 14 fen_model = fromFEN(START_BOARD) if (len(self.squares) == 0): for r in range(0, len(fen_model)): rank = fen_model[r] row = [] for s in range(0, len(rank)): sq = rank[s] _x = sq['_x'] _y = sq['_y'] tx = self.sq_size * _x ty = self.sq_size * _y toggle_color = is_even(_x + 1) ^ is_even(_y + 1) sq_piece = None if (sq['piece']): sq_piece = Piece({ '_x': _x, '_y': _y, 'x': tx, 'y': ty, 'size': self.sq_size - (sq_pad * 2), 'role': sq['piece']['role'], 'color': sq['piece']['color'], 'path': [] }) square = Square({ 'size': self.sq_size, '_x': _x, '_y': _y, 'x': tx, 'y': ty, 'pad': sq_pad, 'font_size': font_size, 'piece': sq_piece, 'color': DARK if toggle_color else LIGHT, 'text_color': LIGHT if toggle_color else DARK, 'label': str(chr(73 - (_y + 1))) + str(_x + 1), 'file': str(_x + 1) if _y == 7 else None, 'rank': str(chr(73 - (_y + 1))) if _x == 0 else None, 'settings': { 'draw_coords': False, 'draw_rankfile': True } }) square.draw() row.append(square) self.squares.append(row) row_blits = list(map(lambda s: (s.surface, (s.x, s.y)), row)) self.surface.blits(row_blits) return self.surface
class Visualizer: def __init__(self, verbose): self._verbose = verbose def _print(self, *args): if self._verbose: print(*args) def init(self, source_path): self._source_file = open(source_path, 'r') self._print('init') self._iteration = 0 self._last_update_time = datetime.now() self._paused = False pygame.init() self._init_flyweights() first_frame = self._read_frame() if first_frame['eof']: raise Exception('Empty grid-viz file') self._last_frame = first_frame self._goals = first_frame['goals'] self._grid_surface = Surface(first_frame['frame_size']) self._screen_size = self._get_screen_size(first_frame['frame_size']) self._print('screen size:', self._screen_size) # font / text init self._iteration_msg_location = (SCREEN_PADDING, SCREEN_PADDING + self._grid_surface.get_size()[1] + 3) self._title_font = font.SysFont(None, 30) self._small_msg_font = font.SysFont(None, 12) # pre-render first frame onto grid surface self._render_frame(first_frame) def render_title_screen(self): if self._source_file is None: raise Exception('Visualizer not initialized') screen = display.set_mode(self._screen_size) txt_img = self._title_font.render('Press ENTER to start', True, WHITE) screen.blit(txt_img, (20, 20)) display.update() if not self._get_continue_signal(): self.quit() return False self._show_grid() return True def next_frame(self, speed, single_frame=False): self._print('In next frame') now = datetime.now() ms_since_update = timedelta_to_milliseconds(now - self._last_update_time) if ms_since_update < speed: self._print('sleeping for', speed - ms_since_update, 'ms') sleep((speed - ms_since_update) / 1000) for evt in event.get(): if evt.type == pygame.QUIT: self._print('Quit event') self.quit() return False if evt.type == pygame.KEYUP: # toggle pause if evt.key == pygame.K_p: self._print('p pressed') self._paused = not self._paused # enter always unpauses if applicable if evt.key == pygame.K_RETURN or evt.key == pygame.K_KP_ENTER: self._print('return pressed') self._paused = False self._print('event queue flushed') # paused, so go away if self._paused: self._print('Paused') return True self._iteration += 1 frame = self._read_frame() if frame['eof']: self._print('Finished reading file') self._render_finished_msg() return False self._last_frame = frame self._render_frame(frame) self._show_grid() if single_frame: self._paused = True return True def quit(self, wait_for_signal=False): if wait_for_signal: self._print('Waiting for shutdown signal') self._get_continue_signal() self._print('Shutting down visualizer') display.quit() self._source_file.close() self._source_file = None def _init_flyweights(self): full_cell_rect = Rect(0, 0, CELL_SIZE, CELL_SIZE) obstacle = Surface((CELL_SIZE, CELL_SIZE)) draw.rect(obstacle, OBSTACLE_COLOR, full_cell_rect) goal = Surface((CELL_SIZE, CELL_SIZE)) draw.rect(goal, GOAL_COLOR, full_cell_rect) agent_rect = Rect(0, 0, AGENT_SIZE, AGENT_SIZE) agent = Surface((AGENT_SIZE, AGENT_SIZE)) draw.rect(agent, AGENT_COLOR, agent_rect) observer_rect = Rect(0, 0, OBSERVER_SIZE, OBSERVER_SIZE) observer = Surface((OBSERVER_SIZE, OBSERVER_SIZE)) draw.rect(observer, OBSERVER_COLOR, observer_rect) self._obstacle = obstacle self._goal = goal self._agent = agent self._observer = observer def _read_frame(self): """Reads a frame from the file. Returns dict of rectangles to be rendered Detects uneven line-lengths and raises exception""" self._print('reading frame') line = self._source_file.readline() while line == '\n': line = self._source_file.readline() if line == '': return {'eof': True} obstacles = [] # holds all obstacle pts goals = [] # holds all goal pts agent = None observer = None agent_offset = (CELL_SIZE - AGENT_SIZE) / 2 observer_offset = (CELL_SIZE - OBSERVER_SIZE) / 2 width = -1 rows = 0 while line != '' and line != '\n': column = 0 for char in line: if char == '\n': continue left, top = (CELL_SIZE * column, CELL_SIZE * rows) agent_left, agent_top = ( left + agent_offset, top + agent_offset, ) observer_left, observer_top = ( left + observer_offset, top + observer_offset, ) if char == '#': obstacles.append((left, top)) elif char == '*': goals.append((left, top)) elif char == '@': agent = (agent_left, agent_top) elif char == 'O' or char == 'o': observer = (observer_left, observer_top) column += 1 if width < 0: width = column elif width != column: self._print(width, column, rows) raise Exception('Uneven frame') rows += 1 line = self._source_file.readline() # if observer is obscured by agent, grab observer's last known location observer_obscured = False if observer is None and self._last_frame is not None and self._last_frame[ 'observer'] is not None: observer_obscured = True if self._last_frame['observer_obscured']: observer = self._last_frame['observer'] else: observer = (agent[0] - agent_offset + observer_offset, agent[1] - agent_offset + observer_offset) return { 'eof': False, 'obstacles': obstacles, 'goals': goals, 'agent': agent, 'observer': observer, 'observer_obscured': observer_obscured, 'frame_size': (CELL_SIZE * (width), CELL_SIZE * (rows)) } def _get_screen_size(self, frame_size): x, y = frame_size padding = 2 * SCREEN_PADDING if frame_size[0] < MIN_SCREEN_SIZE[0] - padding: x = MIN_SCREEN_SIZE[0] else: x += padding if frame_size[1] < MIN_SCREEN_SIZE[1] - padding: y = MIN_SCREEN_SIZE[1] else: y += padding return (x, y) def _get_continue_signal(self): self._print('Getting continue signal') while True: evt = event.wait() if evt.type == pygame.QUIT: return False if evt.type == pygame.KEYUP: if evt.key == pygame.K_RETURN or evt.key == pygame.K_KP_ENTER: return True def _render_frame(self, frame_info): """Renders frame onto grid surface""" self._print('rendering frame onto grid surface') self._grid_surface.fill(WHITE) # erase all previous blit_info = [(self._obstacle, obstacle_loc) for obstacle_loc in frame_info['obstacles']] blit_info += [(self._goal, goal_loc) for goal_loc in self._goals] if frame_info['observer'] is not None: blit_info.append((self._observer, frame_info['observer'])) if frame_info['agent'] is not None: blit_info.append((self._agent, frame_info['agent'])) self._grid_surface.blits(blit_info) def _show_grid(self): self._print('updating to show currend grid surface') screen = display.get_surface() iteration_msg = self._small_msg_font.render( f'Iteration {self._iteration}', True, WHITE) screen.fill(BLACK) # wipe everything screen.blits([(self._grid_surface, GRID_LOC), (iteration_msg, self._iteration_msg_location)]) display.update() self._last_update_time = datetime.now() def _render_finished_msg(self): screen = display.get_surface() finish_msg = self._title_font.render('Playback finished', True, BLUE) screen.blit(finish_msg, (SCREEN_PADDING * 2, SCREEN_PADDING * 2)) display.update()
def render_scene(self, surface: Surface, w_map: world_map.World_Map, sprites: list): """ renders the world using raycasting :param surface: :param world: :param sprites: :param FLOORCAST: :return: None """ to_blit = [] hit_direction = Camera.NORTH_SOUTH self.sprites_in_view = [] self.colliding_sprites = [] check_position = np.ndarray((2, ), np.float32) step_direction = np.ndarray((2, ), np.int32) zbuffer = np.ndarray((surface.get_width(), ), np.float32) draw.rect(surface, (20, 20, 20), (0, 0, surface.get_width(), surface.get_height() // 2)) draw.rect(surface, (40, 40, 40), (0, surface.get_height() // 2, surface.get_width(), surface.get_height() // 2)) for screen_x in range(surface.get_width()): map_pos = self.pos.astype(np.int32) # screen_x is the column of pixels on the screen whose rendering info is being checked direction_variation = 2 * ( screen_x / surface.get_width() ) - 1 # a scalar that represents the amount of deviation a raycasted vector will have from the facing vector # both x and y can be calculated in one line because of numpy magic :D ray_direction = self.facing_vector + self.camera_plane * direction_variation # the facing vector + the direction variation scaled by the camera plane distance_between_gridline_x_y = np.absolute(1 / ray_direction) hit = False #setup initial ray cast if ray_direction[0] < 0: #casting leftwards step_direction[0] = -1 check_position[0] = ( self.pos[0] - map_pos[0] ) * distance_between_gridline_x_y[ 0] # distance from a point within the square outwards to the left side of the square else: #casting rightwards step_direction[0] = 1 check_position[0] = (map_pos[0] + 1 - self.pos[0] ) * distance_between_gridline_x_y[0] if ray_direction[1] < 0: step_direction[1] = -1 check_position[1] = (self.pos[1] - map_pos[1] ) * distance_between_gridline_x_y[1] else: step_direction[1] = 1 check_position[1] = (map_pos[1] + 1 - self.pos[1] ) * distance_between_gridline_x_y[1] #begin casting ray while not hit: if check_position[0] < check_position[1]: check_position[0] += distance_between_gridline_x_y[0] map_pos[0] += step_direction[0] hit_direction = Camera.EAST_WEST else: check_position[1] += distance_between_gridline_x_y[1] map_pos[1] += step_direction[1] hit_direction = Camera.NORTH_SOUTH if w_map.map_data[map_pos[0]][map_pos[1]] != 0: hit = True if hit_direction == Camera.EAST_WEST: distance = (map_pos[0] - self.pos[0] + (1 - step_direction[0]) / 2) / ray_direction[0] else: distance = (map_pos[1] - self.pos[1] + (1 - step_direction[1]) / 2) / ray_direction[1] #distance to surface has been found, now to calculate the height of the wall onscreen if distance == 0: distance = 1 lineHeight = surface.get_height() // distance hit_data = w_map.map_data[map_pos[0]][map_pos[1]] start_y = int(surface.get_height() // 2 - lineHeight // 2) end_y = int(surface.get_height() // 2 + lineHeight // 2) if hit_direction == Camera.EAST_WEST: pixel_pos = self.pos[1] + distance * ray_direction[1] else: pixel_pos = self.pos[0] + distance * ray_direction[0] pixel_pos = (pixel_pos - math.floor(pixel_pos)) blitPixels = w_map.textures[hit_data].subsurface( (int(pixel_pos * w_map.textures[hit_data].get_width()), 0, 1, w_map.textures[hit_data].get_height())).copy() if hit_direction == Camera.EAST_WEST: darkenSurf = Surface((1, blitPixels.get_height())) darkenSurf.set_alpha(128) blitPixels.blit(darkenSurf, (0, 0)) darkenSurf = Surface((1, blitPixels.get_height())) darkenSurf.set_alpha(min(20 * distance, 255)) blitPixels.blit(darkenSurf, (0, 0)) scaled_pixels = transform.scale(blitPixels, (1, min(end_y - start_y, 50000))) to_blit.append((scaled_pixels, (screen_x, start_y))) # fill in z buffer zbuffer[screen_x] = distance # sprite casting sprites = [s for s in sprites if s != self] sprite_draw_order = [(0, 0) for i in range(len(sprites))] for i, sprite in enumerate(sprites): sprite_draw_order[i] = (((self.pos[0] - sprite.pos[0])**2 + (self.pos[1] - sprite.pos[1])**2), i) sprite_draw_order.sort(reverse=True) for i, v in enumerate(sprite_draw_order): seen_sprite = False draw_sprite = sprites[v[1]].get_sprite(self.facing_vector) sprite_pos_rel_to_camera = sprites[v[1]].pos - self.pos if (sprite_pos_rel_to_camera[0] == 0 and sprite_pos_rel_to_camera[1] == 0): sprite_pos_rel_to_camera[0] = 0.01 sprite_pos_rel_to_camera[1] = 0.01 if math.hypot(sprite_pos_rel_to_camera[0], sprite_pos_rel_to_camera[1]) < 0.4: self.colliding_sprites.append(sprites[v[1]]) invDet = 1 / (self.camera_plane[0] * self.facing_vector[1] - self.facing_vector[0] * self.camera_plane[1]) transformX = invDet * ( self.facing_vector[1] * sprite_pos_rel_to_camera[0] - self.facing_vector[0] * sprite_pos_rel_to_camera[1]) transformY = invDet * ( -self.camera_plane[1] * sprite_pos_rel_to_camera[0] + self.camera_plane[0] * sprite_pos_rel_to_camera[1]) spriteScreenPos = int( (surface.get_width() / 2) * (1 + transformX / transformY)) sprite_height = abs(int(surface.get_height() / transformY)) sprite_width = abs(int(surface.get_height() / transformY)) start_x = spriteScreenPos - sprite_width // 2 if start_x < 0: start_x = 0 end_x = spriteScreenPos + sprite_width // 2 if end_x > surface.get_width(): end_x = surface.get_width() - 1 for draw_stripe_x in range(start_x, end_x): textureX = int(256 * (draw_stripe_x - (-sprite_width / 2 + spriteScreenPos)) * draw_sprite.get_width() / sprite_width) / 256 if 0 < transformY and 0 < draw_stripe_x: if transformY < zbuffer[ draw_stripe_x] and draw_stripe_x < surface.get_width( ): spriteSlice = transform.scale( draw_sprite.subsurface( (textureX, 0, 1, draw_sprite.get_height())).copy(), (1, min(sprite_height, 50000))) shaderSurf = Surface( (1, min(spriteSlice.get_height(), 400)), SRCALPHA) shade_val = min( 22 * math.hypot(sprite_pos_rel_to_camera[0], sprite_pos_rel_to_camera[1]), 255) shaderSurf.fill([ 255 - shade_val, 255 - shade_val, 255 - shade_val ]) spriteSlice.blit( shaderSurf, (0, 0), (0, 0) + shaderSurf.get_size(), BLEND_MULT ) # blend mult multiplies each rgb value by the blitter's rgb/255 value to_blit.append( (spriteSlice, (draw_stripe_x, surface.get_height() // 2 - spriteSlice.get_height() // 2))) if draw_stripe_x == int( surface.get_width() // 2 ): # if the stripe goes through the user's crosshairs seen_sprite = True if seen_sprite: self.sprites_in_view.append(sprites[v[1]]) surface.blits(to_blit) return surface
class TileMenuFrame(Sprite): def __init__(self, sx, sy, w, h, camera, bgnd=None, brdr=None): Sprite.__init__(self) self.sx = sx self.sy = sy self.w = w self.h = h self.tw = int(w/TileService.size) self.th = int(h/TileService.size) if(bgnd is None): bgnd = TileService.getTile((0,0)) if(brdr is None): brdr = TileService.getTile((11,13)) self.background = bgnd self.border = brdr self.image = Surface((self.w, self.h)) self.rect = ( self.sx, self.sy, self.w, self.h ) self.fill(self.background) self.addBorder(self.border) camera.add(self) def fill(self, tile): blits = [] for y in range(0, self.th): for x in range(0, self.tw): blits.append((tile, (x*TileService.size, y*TileService.size))) self.image.blits(blit_sequence=blits) def addBorder(self, brdr): self.border = brdr if(self.border is not None): blits = [] for y in range(0,self.th): for x in range(0,self.tw): if(y == 0 or x == 0 or y == self.th-1 or x == self.tw-1): blits.append((self.getBorderTile(x,y),(x*TileService.size,y*TileService.size))) self.image.blits(blit_sequence=blits) def getBorderTile(self, x, y): if(isinstance(self.border, dict) or isinstance(self.border, TileBorder)): if(y == 0 and x == 0): return self.border['tl'].copy() elif(y == 0 and x == self.tw-1): return self.border['tr'].copy() elif(y == 0 and x != self.tw-1): return self.border['t'].copy() elif(y == self.th-1 and x == 0): return self.border['bl'].copy() elif(y == self.th-1 and x == self.tw-1): return self.border['br'].copy() elif(y == self.th-1 and x != self.tw-1): return self.border['b'].copy() elif(y != self.th-1 and x == 0): return self.border['l'].copy() elif(y != self.th-1 and x == self.tw-1): return self.border['r'].copy() else: return TileService.getTile((0,0)) else: return self.border