def _handle_tile_state_click(self, x, y, button): """Handle a click in tile-editing state.""" add = (button == sdl2.SDL_BUTTON_LEFT) tile = self._level.screen_coords_to_tile(vector.Vector(x, y)) if tile: tile_coords = tile.coords height = tile.height + (1 if add else -1) colour = tile.colour else: tile_coords = self._level.screen_coords_to_tile_coords( vector.Vector(x, y)) height = 1 if add else 0 colour = self.colour if self._level.tile_coords_valid(tile_coords): index = self._level.tile_coords_to_array_index(tile_coords) if height > 0: self._level.tiles[index.y, index.x] = typingdefense.level.Tile( self._app, self._level.cam, tile_coords, height, colour) else: self._level.tiles[index.y, index.x] = None
def _setup_move(self, timer): if self.next_tile: self._start_pos = vector.Vector(self.current_tile.x, self.current_tile.y, self.current_tile.top) self._end_pos = vector.Vector(self.next_tile.x, self.next_tile.y, self.next_tile.top) distance = (self._end_pos - self._start_pos).magnitude self._move_start = timer.time + self._scaled_pause() self._move_end = self._move_start + distance / self._scaled_speed()
def _build_paths(self): """Calculate paths from each tile to the base.""" # Clear any previous path info for _, tile in numpy.ndenumerate(self.tiles): if tile: tile.path_next = None # TODO: Start a 0,0 for now, but eventually will have to work out where # the base is and start there. start = self.lookup_tile(vector.Vector(0, 0)) if not start: return # TODO: consider height frontier = deque([start]) visited = set([start]) while len(frontier) > 0: tile = frontier.popleft() for nxt in [ t for t in self.tile_neighbours(tile) if t.empty and t not in visited ]: frontier.append(nxt) visited.add(nxt) nxt.path_next = tile
def _handle_wave_state_click(self, x, y, button): """Handle a click in wave-editing state.""" add = (button == sdl2.SDL_BUTTON_LEFT) tile = self._level.screen_coords_to_tile(vector.Vector(x, y)) if add and tile: if self.phase not in tile.waves: wave = enemy.Wave(self._app, self._level, tile, enemy_type=self.enemy_type) # Extend the wave list if it is not long enough. self._level.waves += [[]] * (self.phase + 1 - len(self._level.waves)) self._level.waves[self.phase].append(wave) tile.waves[self.phase] = wave self.selected_wave = wave else: self.selected_wave = tile.waves[self.phase] if not add and tile and self.phase in tile.waves: wave = tile.waves[self.phase] self._level.waves[self.phase].remove(wave) del(tile.waves[self.phase])
def __init__(self, app, level, tile): super().__init__(app, level, tile, SlowTower._COLOUR) self._level = level self._coords = vector.Vector(tile.x, tile.y) for t in level.tile_neighbours(tile): t.slow = True
def __init__(self, app, cam, coords, height, colour): """Construct a (hexagonal) tile. coords is a vector containing the horizontal coordinates of the tile, using axial coordinates. height is the number of stacks in the tile. """ self.coords = coords self.height = height self.colour = colour self.path_next = None self.tower = None self._shader = glutils.ShaderInstance( app, 'level.vs', 'level.fs', [('transMatrix', GL.GL_FLOAT_MAT4, cam.trans_matrix_as_array()), ('colourIn', GL.GL_FLOAT_VEC4, None)]) self._hex = glutils.Hex(vector.Vector(self.x, self.y, 0), Tile.SIZE, Tile.DEPTH, height) self.outline_colour = colour self.face_colour = copy.copy(self.outline_colour) self.face_colour.s = self.face_colour.s / 2 # Dictionary of waves, keyed by the level phase in which they appear. self.waves = {} # Whether the tile is a 'slow movement' tile. self.slow = False
def world_to_tile_coords(world_coords): """Convert world (x, y) coordinates to tile (q, r) coordinates. Note that this is a 2D conversion only.""" q = (world_coords.x * math.sqrt(3) / 3 - world_coords.y / 3) / Tile.SIZE r = (world_coords.y * 2 / 3) / Tile.SIZE return _hex_round(vector.Vector(q, r))
def __init__(self, app, cam, tile, origin, z): self.health = Base.START_HEALTH self.tile = tile self._shader = glutils.ShaderInstance( app, 'level.vs', 'level.fs', [('transMatrix', GL.GL_FLOAT_MAT4, cam.trans_matrix_as_array())]) self._hex = glutils.Hex(vector.Vector(tile.x, tile.y, 0), Tile.SIZE * 0.8, Tile.DEPTH, 2)
def screen_coords_to_tile(self, coords): """Work out which tile a given point in screen coordinates is in.""" pixel_info = self._picking_texture.read(coords.x, coords.y) # The blue value will be 0 if no tile was hit if pixel_info[2] == 0: return None # The q and r coordinates are stored in the r and g values, respectively return self.lookup_tile(vector.Vector(pixel_info[0], pixel_info[1]))
def __init__(self, app, level, tile, speed, move_pause, value, damage, colour, health=1, words=1, wordlength=phrasebook.PhraseBook.SHORT_PHRASE): # TODO:temp self.prev_time = 0 self.prev_origin = vector.Vector(0, 0, 0) self._app = app self._level = level self._tile = tile # Phrase variables self._words = words self._wordlength = wordlength self.phrase = None # Movement variables self.origin = vector.Vector(tile.x, tile.y, tile.top) self.prev_tile = None self.current_tile = tile self.next_tile = tile.path_next self._speed = speed self._move_pause = move_pause self._start_pos = None self._end_pos = None self._move_start = 0 self._move_end = 0 # Graphics variables self._shader = glutils.ShaderInstance( app, 'level.vs', 'level.fs', [('transMatrix', GL.GL_FLOAT_MAT4, None), ('colourIn', GL.GL_FLOAT_VEC4, colour)]) self._hex = glutils.Hex(vector.Vector(0, 0, 0), 0.5, 1) self.health = health self.damage = damage self.unlink = False self.value = value # Initial setup self._setup_move(level.timer) self._setup_phrase()
def __init__(self, app, level, tile, colour): self._shader = glutils.ShaderInstance( app, 'level.vs', 'level.fs', [('transMatrix', GL.GL_FLOAT_MAT4, level.cam.trans_matrix_as_array()), ('colourIn', GL.GL_FLOAT_VEC4, colour)]) self._hex = glutils.Hex(vector.Vector(tile.x, tile.y, tile.top), 0.5, 2, stacks=4)
def __init__(self, app, editor): self._editor = editor self._colourbutton = _ColourButton(app, vector.Vector(10, 10)) font = app.resources.load_font('menufont.fnt') self._enemy_type_text = text.Text2D(app, font, '', 0, 0, 24) self._enemy_count_text = text.Text2D(app, font, '', 0, 24, 24) self._start_time_text = text.Text2D(app, font, '', 0, 48, 24) self._spawn_gap_text = text.Text2D(app, font, '', 0, 72, 24) self._phase_text = text.Text2D(app, font, '', 0, app.window_height - 24, 24)
def draw(self): coords = vector.Vector(self.origin.x, self.origin.y, self.origin.z) t = util.Transform(coords) m = self._level.cam.trans_matrix * t.matrix self._shader.set_uniform('transMatrix', numpy.asarray(m).reshape(-1), download=False) with self._shader.use(): self._hex.draw() self.phrase.draw(coords)
def tile_neighbours(self, tile): """Find the neighbouring tiles for a given tile. Takes a Tile and returns a list of Tiles. Does not consider whether a given tile is empty or not. """ dirs = [(+1, 0), (+1, -1), (0, -1), (-1, 0), (-1, 1), (0, 1)] neighbours = [] for d in dirs: neighbour_coords = vector.Vector(tile.q + d[0], tile.r + d[1]) neighbour = self.lookup_tile(neighbour_coords) if neighbour: neighbours.append(neighbour) return neighbours
def on_click(self, x, y, button): """Handle a mouse click.""" if self.state == Level.State.build: hit_hud = self._hud.on_click(x, y) if not hit_hud: tile = self.screen_coords_to_tile(vector.Vector(x, y)) if tile and tile.empty: # TODO: Check if the tower ends up leaving no route to the # base if (self.tower_creator is not None and self.money >= self.tower_creator.COST): tower = self.tower_creator(self._app, self, tile) self._towers.append(tower) tile.tower = tower self.money -= tower.COST
def load(self): """Load the level.""" self._min_coords = vector.Vector(-100, -100) self._max_coords = vector.Vector(100, 100) width = self._max_coords.x - self._min_coords.x + 1 height = self._max_coords.y - self._min_coords.y + 1 self.tiles = numpy.empty([height, width], dtype=object) try: with open('resources/levels/test_level.tdl', 'r') as f: lvl_info = json.load(f) # Load tiles for tile_info in lvl_info['tiles']: coords = vector.Vector(tile_info['q'], tile_info['r']) colour = util.Colour(tile_info['colour']['r'], tile_info['colour']['g'], tile_info['colour']['b'], tile_info['colour']['a']) idx = self.tile_coords_to_array_index(coords) self.tiles[idx.y, idx.x] = Tile(self._app, self.cam, coords, tile_info['height'], colour) # Load Waves phase_idx = 0 if 'waves' in lvl_info: for phase_info in lvl_info['waves']: waves = [] for wave_info in phase_info: coords = vector.Vector(wave_info['q'], wave_info['r']) tile = self.lookup_tile(coords) wave = enemy.Wave( self._app, self, tile, enemy_count=wave_info['enemy_count'], start_time=wave_info['start_time'], spawn_gap=wave_info['spawn_gap'], enemy_type=wave_info['enemy_type']) tile.waves[phase_idx] = wave waves.append(wave) self.waves.append(waves) phase_idx += 1 except FileNotFoundError: pass tile = self.lookup_tile(vector.Vector(0, 0)) self.base = Base(self._app, self.cam, tile, vector.Vector(0, 0), Tile.HEIGHT) self.money = 500
def _cube_round(fc): """Round fractional cube-format hex coordinates.""" rx = round(fc.x) ry = round(fc.y) rz = round(fc.z) x_diff = abs(rx - fc.x) y_diff = abs(ry - fc.y) z_diff = abs(rz - fc.z) if x_diff > y_diff and x_diff > z_diff: rx = -ry - rz elif y_diff > z_diff: ry = -rx - rz else: rz = -rx - ry return vector.Vector(rx, ry, rz)
def unproject(self, screen_coords, world_z): # Convert from screen coords to NDCs x = screen_coords.x * 2 / self.screen_width - 1 y = screen_coords.y * 2 / self.screen_height - 1 inv_trans = np.linalg.inv(self.trans_matrix) near_coords = np.array([[x], [y], [0], [1]]) far_coords = np.array([[x], [y], [1], [1]]) world_near = inv_trans * near_coords world_far = inv_trans * far_coords # Perspective divide world_near /= world_near[3, 0] world_far /= world_far[3, 0] world_neartofar = world_far - world_near ratio = (world_z - world_near[2, 0]) / world_neartofar[2, 0] return vector.Vector( world_near[0, 0] + (world_neartofar[0, 0] * ratio), world_near[1, 0] + (world_neartofar[1, 0] * ratio), world_near[2, 0] + (world_neartofar[2, 0] * ratio))
def _cube_to_hex(c): """Convert cube-format hex coordinates to hex.""" return vector.Vector(c.x, c.z)
def _hex_to_cube(h): """Convert axial-format hex coordinates to cube-format.""" return vector.Vector(h.q, -h.q - h.r, h.r)
def tile_coords_to_array_index(self, coords): """Work out the array slot for a given set of axial tile coords.""" return vector.Vector(coords.q - self._min_coords.q, coords.r - self._min_coords.r)
def __init__(self, app, level, tile): super().__init__(app, level, tile, MoneyTower._COLOUR) self._level = level self._tile = tile self._coords = vector.Vector(tile.x, tile.y) self._last_fire = 0