def kill(self): bg_tile = None if hasattr(self, 'bg_name'): bg_tile = BgTile(self.map, self.pos, self.bg_name, self.bg_grey) self.map.data[vec2int(self.pos)] = bg_tile for group in self.groups: self.map.groups[group].pop(vec2int(self.pos)) self.map.clear_path_cache()
def can_stand(self, pos): tile = self.data[vec2int(pos)] tile_below = self.data[vec2int(pos + (0, 1))] if tile is not None and tile.in_group('passable'): return True if tile_below and tile_below.in_group('block'): if (tile is None) or (tile and not tile.in_group('block')): return True return False
def __init__(self, map, pos, groups): self.map = map self.pos = pos # 用于 kill 自己 self.map.data[vec2int(pos)] = self self.groups = list(groups) for group in self.groups: self.map.groups[group][vec2int(pos)] = self self.hp = 1 self.map.clear_path_cache()
def find(self, pos): path = self.game.map.path_finding(pos) if vec2int(self.pos) in path: vel = path[vec2int(self.pos)] if vel: self.move(vel, True) return 1 # 没到达目的地 return 2 # 到达目的地 return 0 # 没有路径可以走
def light(self, pos, view_radius=5, skip=6): # TODO light fade for angle in range(0, 360, skip): v = vec(math.cos(angle * 0.01745), math.sin(angle * 0.01745)) o = pos + (0.5, 0.5) block = False for i in range(view_radius): if not block: tile = self.data[vec2int(o)] self.groups['visible'][vec2int(o)] = tile self.groups['visited'][vec2int(o)] = tile # if tile and tile.in_group('block'): # block = True o += v if not self.in_map(o): block = True
def consume_woods(self, n): for i, wood_in_res in enumerate([ wood for wood in self.woods if vec2int(wood.pos + vec(0, 1)) in self.map.groups['resource_mark'] ]): if i < n: wood_in_res.kill()
def __init__(self, map, pos): groups = ('passable', 'ladder', 'destroyable') bg_tile = map.data[vec2int(pos)] if bg_tile: self.bg_name = bg_tile.image_name self.bg_grey = bg_tile.grey Tile.__init__(self, map, pos, groups) self.image_name = 'ladder'
def __init__(self, map, pos, range=5): groups = ('light', 'torch', 'destroyable') bg_tile = map.data[vec2int(pos)] if bg_tile: self.bg_name = bg_tile.image_name self.bg_grey = bg_tile.grey Tile.__init__(self, map, pos, groups) self.image_name = 'tochLit' self.range = range
def update(self): self.rect.topleft = config.TILESIZE * self.pos self.float_pos = vec2int(config.TILESIZE * self.pos) if self.owner: self.pos = vec(self.owner.pos) # 掉落,用来临时解决脚下方块消失的问题 while not self.game.map.can_stand(self.pos): self.pos.y += 1 self.v = config.TILESIZE * self.pos - self.rect.topleft
def __init__(self, game, pos): self._layer = 1 self.groups = game.all_sprites, game.items, game.woods pg.sprite.Sprite.__init__(self, self.groups) self.game = game self.origin_image = self.game.img['wood'] self.image = self.origin_image self.rect = self.image.get_rect() self.pos = pos self.float_pos = vec2int(pos) self.v = vec() self.rect.topleft = self.pos * config.TILESIZE self.owner = None
def __init__(self, map, pos, image_name, worker=None): groups = ( 'build_mark', 'build_mark_noworker', ) bg_tile = map.data[vec2int(pos)] if bg_tile: self.bg_name = bg_tile.image_name self.bg_grey = bg_tile.grey Tile.__init__(self, map, pos, groups) self.image_name = image_name self.alpha = 128 self.hp = 0
def path_finding(self, target): if vec2int(target) in self.path_cache: return self.path_cache[vec2int(target)] frontier = deque() frontier.append(target) path = dict() path[vec2int(target)] = None while len(frontier) > 0: current = frontier.popleft() for next in self.find_neighbors(current, (self.can_stand, )): if vec2int(next) not in path: frontier.append(next) path[vec2int(next)] = current - next if vec2int(target) not in self.path_cache: self.path_cache[vec2int(target)] = path return path
def __init__(self, map, pos): if not map.in_map(vec2int(pos)) or map.data[vec2int(pos)] is not None: return groups = ('leaf', ) Tile.__init__(self, map, pos, groups) self.image_name = 'leaves_transparent'
def next_turn(self): # ----- 遍历标记,为标记雇佣 imp ----- for mark in union_dicts(self.map.groups['dig&cut_mark'], self.map.groups['build_mark_noworker']): tile = self.map.data[mark] neighbors = self.map.find_neighbors(tile.pos, (self.map.can_stand, )) found = False for dest in neighbors: if not found: path = self.map.path_finding(dest) for imp in self.imps: if not found: if imp.task is None and vec2int(imp.pos) in path: if tile and (tile.in_group('diggable') or tile.in_group('destroyable')): imp.task = Dig(self.map, tile.pos, dest) found = True elif tile and tile.in_group('cuttable'): imp.task = Cut(self.map, tile.pos, dest) found = True elif isinstance(tile, BuildMark): if (tile.image_name == 'torch') or \ (tile.image_name == 'ladder' and self.resource['woods'] >= 1) or \ (tile.image_name == 'shelf' and self.resource['woods'] >= 3) or \ (tile.image_name == 'bed' and self.resource['woods'] >= 4): imp.task = Build( self.map, tile.pos, dest) tile.worker = imp tile.remove_from_group( 'build_mark_noworker') found = True # # ----- 更新小弟目的地 ----- # for imp in self.imps: # if isinstance(imp.task, Dig) or isinstance(imp.task, Cut): # imp.task.path = {} # neighbors = self.map.find_neighbors(imp.task.target, (self.map.can_stand,)) # found = False # for dest in neighbors: # if not found: # path = self.map.path_finding(dest) # if vec2int(imp.pos) in path: # imp.task.path = path # found = True for wood in self.woods: tile_below = self.map.data[vec2int(wood.pos + (0, 1))] if not wood.owner and tile_below and not tile_below.in_group( 'resource_mark'): path = self.map.path_finding(wood.pos) found = False for imp in self.imps: if not found: if imp.task is None and vec2int(imp.pos) in path and \ len([i for i in imp.inventories if isinstance(i, Wood)]) < 1: imp.task = Carry(self.map, wood.pos, wood.pos, item_to_find=wood) for mark in self.map.groups['resource_mark']: path = self.map.path_finding(vec(mark) + (0, -1)) for wood in self.woods: if wood.owner and vec2int(wood.owner.pos) in path: wood.owner.task = Carry(self.map, vec(mark) + (0, -1), vec(mark) + (0, -1), item_to_find=None) self.all_sprites.update() self.map.groups['visible'] = {} for imp in self.imps: self.map.light(imp.pos) self.map.light_torch()
def update(self): if not self.pause: now = pg.time.get_ticks() if now - self.last_update > self.game_speed: self.last_update = now self.next_turn() for sprite in self.all_sprites: sprite.float_pos += sprite.v / ( (self.game_speed - 14) / 1000) / config.FPS sprite.rect.topleft = sprite.float_pos mouse_pos = vec(pg.mouse.get_pos()) self.cursor_pos = vec( vec2int((mouse_pos - self.camera.offset) / config.TILESIZE)) tile = self.map.data[vec2int(self.cursor_pos)] # drag camera if self.dragging_camera: mouse_vector = mouse_pos - self.last_mouse_pos self.camera.update(mouse_vector) # game stuff if self.map.in_bounds(self.cursor_pos) and self.map.in_view( self.cursor_pos): if self.mode == 'dig&cut' and tile and ( tile.in_group('destroyable') or tile.in_group('diggable') or tile.in_group('cuttable')): if self.dragging == 1: if not tile.in_group('dig&cut_mark'): tile.add_to_group('dig&cut_mark') self.snd['mark'].play() elif self.dragging == -1: if tile.in_group('dig&cut_mark'): tile.remove_from_group('dig&cut_mark') elif self.mode == 'ladder': if self.dragging == 1: if tile is None or isinstance(tile, BgTile): # Ladder(self.map, self.cursor_pos) BuildMark(self.map, self.cursor_pos, 'ladder') elif self.dragging == -1: if isinstance(tile, BuildMark) and tile.image_name == 'ladder': tile.kill() elif self.mode == 'shelf': if self.dragging == 1: if tile is None or isinstance(tile, BgTile): BuildMark(self.map, self.cursor_pos, 'shelf') elif self.dragging == -1: if isinstance(tile, BuildMark) and tile.image_name == 'shelf': tile.kill() elif self.mode == 'bed': if self.dragging == 1: if tile is None or isinstance(tile, BgTile): BuildMark(self.map, self.cursor_pos, 'bed') elif self.dragging == -1: if isinstance(tile, BuildMark) and tile.image_name == 'bed': tile.kill() elif self.mode == 'resource' and tile and tile.in_group('earth'): if self.dragging == 1: if not tile.in_group('resource_mark') and \ self.map.can_stand(tile.pos + (0, -1)): tile.add_to_group('resource_mark') self.snd['mark'].play() elif self.dragging == -1: if tile.in_group('resource_mark'): tile.remove_from_group('resource_mark') self.snd['unmark'].play() self.last_mouse_pos = mouse_pos
def in_group(self, group): if vec2int(self.pos) in self.map.groups[group]: return True return False
def update(self): # Control # keys = pg.key.get_pressed() # if keys[pg.K_LEFT]: # self.move(x = -1) # elif keys[pg.K_RIGHT]: # self.move(x = 1) # elif keys[pg.K_UP]: # self.move(y = -1) # elif keys[pg.K_DOWN]: # self.move(y = 1) self.rect.topleft = config.TILESIZE * self.pos self.float_pos = vec2int(config.TILESIZE * self.pos) pos = vec(self.pos) # 拷贝原坐标,用于后面更新朝向 if isinstance(self.task, Dig) or isinstance(self.task, Cut): if vec2int(self.task.target ) not in self.game.map.groups['dig&cut_mark']: self.task = None else: find_res = self.find(self.task.dest) if find_res == 2: # 到达目的地,开始敲 # random.choice(self.game.snd['digging']).play() if isinstance(self.task, Dig): random.choice(self.game.snd['digging']).play() elif isinstance(self.task, Cut): self.game.snd['cut_tree'].play() if self.game.map.data[vec2int( self.task.target)].hit(-1) <= 0: if isinstance(self.task, Cut): self.game.snd['tree_down'].play() neighbors = self.game.map.find_neighbors( self.task.target, (self.game.map.can_stand, )) neighbors.append(self.task.target) for i, neighbor in enumerate(neighbors): if i < 3: Wood(self.game, neighbor) elif find_res == 1: # 在路上 pass elif find_res == 0: # 无法到达,游荡 self.task = None elif isinstance(self.task, Carry): if not self.task.item_to_find: # 去放木头 if vec2int(self.task.target + (0, 1)) not in self.game.map.groups['resource_mark']: self.task = None else: find_res = self.find(self.task.dest) if find_res == 2: wood = [ i for i in self.inventories if isinstance(i, Wood) ][0] self.inventories.remove(wood) wood.owner = None self.task = None elif find_res == 1: pass elif find_res == 0: self.task = None # if find_res != 1: self.state = Hangout() 用这个简练点? else: # 去捡木头 if self.task.item_to_find.owner is not None: self.task = None else: find_res = self.find(self.task.dest) if find_res == 2: self.task.item_to_find.owner = self self.inventories.append(self.task.item_to_find) self.task = None elif find_res == 1: pass elif find_res == 0: self.task = None elif isinstance(self.task, Build): if vec2int(self.task.target ) not in self.game.map.groups['build_mark']: self.task = None else: find_res = self.find(self.task.dest) if find_res == 2: # 到达目的地,开始敲 random.choice(self.game.snd['build']).play() tile_to_built = self.game.map.data[vec2int( self.task.target)] hp = tile_to_built.hit(+1) if tile_to_built.image_name == 'ladder' and hp >= 3: tile_to_built.kill() Ladder(self.game.map, tile_to_built.pos) self.game.consume_woods(1) elif tile_to_built.image_name == 'shelf' and hp >= 6: tile_to_built.kill() Shelf(self.game.map, tile_to_built.pos) self.game.consume_woods(3) elif tile_to_built.image_name == 'bed' and hp >= 6: tile_to_built.kill() Bed(self.game.map, tile_to_built.pos) self.game.consume_woods(4) elif tile_to_built.image_name == 'torch' and hp >= 1: tile_to_built.kill() Torch(self.game.map, tile_to_built.pos) elif find_res == 1: # 在路上 pass elif find_res == 0: # 无法到达,游荡 self.task = None elif self.task is None: self.hangout() # 掉落,用来临时解决脚下方块消失的问题 while not self.game.map.can_stand(self.pos): self.pos.y += 1 # repair image orient if self.pos.x != pos.x: if self.pos.x < pos.x: self.origin_image = self.standing_image_l elif self.pos.x > pos.x: self.origin_image = self.standing_image_r self.v = config.TILESIZE * self.pos - self.rect.topleft
def events(self): tile = self.map.data[vec2int(self.cursor_pos)] for e in pg.event.get(): # ----- 退出事件 ----- if e.type == pg.QUIT: if self.playing: self.playing = False self.running = False # ----- 退出事件 ----- # ----- 鼠标按下事件 ----- if e.type == pg.MOUSEBUTTONDOWN: # e.button 1左键 2中键 3右键 4滚轮上 5滚轮下 if e.button == 1: if self.map.in_bounds( self.cursor_pos) and self.map.in_view( self.cursor_pos): if self.mode == 'imp': if self.map.can_stand(self.cursor_pos): Imp(self, self.cursor_pos) elif self.mode == 'shelf': if tile is None or isinstance(tile, BgTile): self.dragging = 1 elif isinstance( tile, BuildMark) and tile.image_name == 'ladder': self.dragging = -1 elif self.mode == 'bed': if tile is None or isinstance(tile, BgTile): self.dragging = 1 elif isinstance( tile, BuildMark) and tile.image_name == 'bed': self.dragging = -1 elif self.mode == 'ladder': if tile is None or isinstance(tile, BgTile): self.dragging = 1 elif isinstance( tile, BuildMark) and tile.image_name == 'ladder': self.dragging = -1 elif self.mode == 'dig&cut' and tile: if tile.in_group('dig&cut_mark'): self.dragging = -1 else: self.dragging = 1 elif self.mode == 'resource' and tile and tile.in_group( 'earth'): if tile.in_group('resource_mark'): self.dragging = -1 else: self.dragging = 1 elif self.mode == 'torch': if isinstance(tile, BgTile): BuildMark(self.map, tile.pos, 'torch') elif isinstance(tile, Torch): tile.kill() elif self.mode == 'remove' and tile is not None: tile.kill() if e.button == 3: self.dragging_camera = True if e.button == 4: self.mode = self.modes[(self.modes.index(self.mode) - 1) % len(self.modes)] self.snd['shift'].play() if e.button == 5: self.mode = self.modes[(self.modes.index(self.mode) + 1) % len(self.modes)] self.snd['shift'].play() # ----- 鼠标按下事件 ----- # ----- 鼠标放开事件 ----- if e.type == pg.MOUSEBUTTONUP: if e.button == 1: if self.dragging: self.dragging = False if e.button == 2: # debug pass if e.button == 3: self.dragging_camera = False # ----- 鼠标放开事件 ----- # ----- 键盘按下事件 ----- if e.type == pg.KEYDOWN: if e.key == pg.K_SPACE: self.pause = False if self.pause else True if e.key == pg.K_1: self.game_speed = 800 if e.key == pg.K_2: self.game_speed = 600 if e.key == pg.K_3: self.game_speed = 400 if e.key == pg.K_4: self.game_speed = 200 if e.key == pg.K_z: if config.TILESIZE == 48: config.TILESIZE = 32 self.camera.offset += (self.cursor_pos.x * 16, self.cursor_pos.y * 16) else: config.TILESIZE = 48 self.camera.offset -= (self.cursor_pos.x * 16, self.cursor_pos.y * 16) self.camera.repair() if e.key == pg.K_d: self.mode = 'dig&cut' if e.key == pg.K_i: self.mode = 'imp' if e.key == pg.K_l: self.mode = 'ladder' if e.key == pg.K_r: self.mode = 'resource' if e.key == pg.K_c: self.mode = 'torch'
def add_to_group(self, group): self.map.groups[group][vec2int(self.pos)] = self self.groups.append(group)
def draw(self, camera): # 返回绘制的地图块数量 drawn = 0 # 计算屏幕笼罩区域 row_start = int(-camera.offset.y / config.TILESIZE) row_end = int((-camera.offset.y + HEIGHT) / config.TILESIZE) + 1 col_start = int(-camera.offset.x / config.TILESIZE) col_end = int((-camera.offset.x + WIDTH) / config.TILESIZE) + 1 for row in range(row_start, row_end): for col in range(col_start, col_end): pos = vec(col, row) scr_pos = pos * config.TILESIZE + camera.offset tile = self.data[vec2int(pos)] if vec2int( pos) in self.data else None # 防止渲染出界 if vec2int(pos) in self.groups['visited'] or True: if tile is not None: if hasattr(tile, 'bg_name'): image = self.resize(self.image[tile.bg_name]) if hasattr(tile, 'bg_grey') and tile.bg_grey: image = self.set_grey(image, 100) fg_image = self.image[tile.image_name].copy() if hasattr(tile, 'alpha'): fg_image.set_alpha(tile.alpha) image.blit(self.resize(fg_image), (0, 0)) else: image = self.resize(self.image[tile.image_name]) if hasattr(tile, 'alpha'): image.set_alpha(tile.alpha) if hasattr(tile, 'grey') and tile.grey: image = self.set_grey(image, 100) self.game.screen.blit(image, scr_pos) drawn += 1 # draw forbidden area if not self.in_bounds(pos): fbd_tile = self.resize(self.fbd_tile) self.game.screen.blit(fbd_tile, scr_pos) # draw resource area if tile and tile.in_group('resource_mark'): image = self.resize(self.image['resource_mark']) self.game.screen.blit(image, scr_pos) if vec2int(pos) not in self.groups['visible']: image = self.black_tile.copy() image.set_alpha(180) self.game.screen.blit(self.resize(image), scr_pos) else: image = self.black_tile.copy() self.game.screen.blit(self.resize(image), scr_pos) # draw mark if tile and tile.in_group('dig&cut_mark'): dirs = [ vec(x, y) for x in range(-1, 2) for y in range(-1, 2) ] dirs.remove(vec(0, 0)) found = 0 for neighbor in [tile.pos + dir for dir in dirs]: neighbor = self.data[vec2int(neighbor)] if neighbor and self.can_stand(neighbor.pos): found += 1 if found: self.game.screen.blit( self.resize(self.game.img['dig_mark_hammer']), scr_pos) else: self.game.screen.blit( self.resize(self.game.img['dig_mark']), scr_pos) return drawn
def in_view(self, pos): if vec2int(pos) in self.groups['visited']: return True return False
def draw(self): # draw debug HUD pg.display.set_caption("{:.2f}".format(self.clock.get_fps())) # pg.display.set_caption("camera({}, {})".format(self.camera.offset.x, self.camera.offset.y)) # pg.display.set_caption( # "({},{},{})".format(self.cursor_pos.x, self.cursor_pos.y, # (type(self.map.data[vec2int(self.cursor_pos)])))) self.screen.blit(pg.transform.scale(self.img['sky_top'], (WIDTH, 100)), (0, 0)) self.screen.blit(self.img['sky'], (0, 100)) self.screen.blit(self.img['sky'], (512, 100)) self.screen.blit( pg.transform.scale(self.img['sky_bottom'], (WIDTH, 512)), (0, 612)) tiles = self.map.draw(self.camera) # pg.display.set_caption(str(tiles)) # self.all_sprites.draw(self.screen) for sprite in self.all_sprites: if isinstance(sprite, Wood) and sprite.owner: # 隐藏被捡起来的木头 continue self.screen.blit(sprite.image, self.camera.apply(sprite)) # draw target box and mouse cursor target = pg.Rect( self.cursor_pos.x * config.TILESIZE + self.camera.offset.x, self.cursor_pos.y * config.TILESIZE + self.camera.offset.y, config.TILESIZE, config.TILESIZE) pg.draw.rect(self.screen, WHITE, target, 2) self.draw_mode_icon(self.mode) # draw game speed if self.pause: self.draw_text('PAUSE', 32, RED, vec(WIDTH / 2, 16), align='mid') else: self.draw_text('SPEED:', 32, WHITE, vec(WIDTH / 2 - 130, 16), align='mid') for i in range(1, 5): if 1000 - (200 * i) == self.game_speed: self.draw_text('x' + str(i), 38, BROWN, vec(WIDTH / 2 - 100 + 56 * i, 16), align='mid') else: self.draw_text('x' + str(i), 32, WHITE, vec(WIDTH / 2 - 100 + 56 * i, 16), align='mid') # draw resource # count woods self.resource['woods'] = 0 for wood in self.woods: if vec2int(wood.pos + vec(0, 1)) in self.map.groups['resource_mark']: self.resource['woods'] += 1 count_woods = self.resource['woods'] for bm in self.map.groups['build_mark']: bm = self.map.data[bm] if bm.image_name == 'ladder': count_woods -= 1 elif bm.image_name == 'shelf': count_woods -= 3 elif bm.image_name == 'bed': count_woods -= 4 self.screen.blit(pg.transform.scale(self.img['wood'], (24, 24)), (16, HEIGHT - 30)) if count_woods >= 0: self.draw_text('x' + str(count_woods), 24, WHITE, (40, HEIGHT - 24)) else: self.draw_text('x' + str(count_woods), 24, RED, (40, HEIGHT - 24)) self.screen.blit(self.img['cursor'], self.last_mouse_pos - (15, 10)) pg.display.flip()
def remove_from_group(self, group): self.map.groups[group].pop(vec2int(self.pos)) self.groups.remove(group)