def __init__(self, count, pos_x, pos_y, *groups): if count > 1: label = "%d" % count else: label = "" pos_y -= 2 spr = data.load_image("ghost.png") w, h = spr.get_size() fnt = data.load_image("digits.png") fnt_w = 4 fnt_h = 5 txt_x = w + 1 txt_y = 2 self.image = pygame.Surface((txt_x + fnt_w * len(label), h), pygame.SRCALPHA) self.image.blit(spr, (0, 0)) for i, c in enumerate(label): fnt_x = int(c) * fnt_w self.image.blit(fnt, (txt_x + i * fnt_w, txt_y), (fnt_x, 0, fnt_w, fnt_h)) self.rect = pygame.Rect(pos_x, pos_y, *self.image.get_size()) self.targ_y = pos_y - DEAD_TTL // 2 pygame.sprite.Sprite.__init__(self, *groups)
def draw(self, m): self.buf = pygame.Surface((m.width * GRID_W, m.height * GRID_H)) self.buf.fill(0) for (x, y), c in m.grid.items(): self.buf.blit(self.tls.get("field"), (x * GRID_W, y * GRID_H)) for x in range(m.width): for y in range(m.height): c = m.grid[x, y] if c.view == "field": continue self.draw_one(x, y, c)
def main(init_mode=title.Title): pygame.mixer.pre_init(44100) pygame.init() pygame.mixer.set_num_channels(32) mouse.init() music.load() music.reset("major") music.play() pygame.display.set_caption(GAME_NAME) pygame.mouse.set_visible(False) size = SCREEN_W * SCALE_FACTOR, SCREEN_H * SCALE_FACTOR disp = pygame.display.set_mode(size, DOUBLEBUF) temp = pygame.Surface((SCREEN_W, SCREEN_H)) uibg = data.load_image("uibg.png") mode = init_mode() while mode is not None: for ev in pygame.event.get(): if ev.type == QUIT: return if ev.type == pygame.MOUSEBUTTONUP: mode.handle_click(ev) temp.fill(SCREEN_BG) temp.blit(uibg, (0, GRID_MAX_H * GRID_H + 1)) mode = mode.update(temp) mouse.draw(temp) pygame.transform.scale(temp, disp.get_size(), disp) pygame.display.flip() print "Bye."
def __init__(self, name): self.name = name self.conf = data.load_json(name) self.surf = data.load_image(self.conf["image"]) self.tiles = defaultdict(list) self.dummy = pygame.Surface((GRID_W, GRID_H)) self.dummy.fill((255, 0, 0)) for name, coords in self.conf["tiles"].items(): for ix, iy in coords: x = ix * GRID_W y = iy * GRID_H tile = pygame.Surface((GRID_W, GRID_H), SRCALPHA) tile.blit(self.surf, (0, 0), (x, y, GRID_W, GRID_H)) self.tiles[name].append(tile) if pygame.version.vernum >= (2, 0, 0): # pygame_sdl2 has trouble with auto-generate stuff below, so we skip it return gen = defaultdict(list) # straits for tile in self.tiles["road-hh"]: tile = pygame.transform.flip(tile, True, False) gen["road-hh"].append(tile) tile = pygame.transform.flip(tile, False, True) gen["road-hh"].append(tile) tile = pygame.transform.flip(tile, True, False) gen["road-hh"].append(tile) for tile in self.tiles["road-vv"]: tile = pygame.transform.flip(tile, True, False) gen["road-vv"].append(tile) tile = pygame.transform.flip(tile, False, True) gen["road-vv"].append(tile) tile = pygame.transform.flip(tile, True, False) gen["road-vv"].append(tile) # turns for tile in self.tiles["road-tl"]: tile = pygame.transform.rotate(tile, 90) gen["road-bl"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-br"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-tr"].append(tile) for tile in self.tiles["road-tr"]: tile = pygame.transform.rotate(tile, 90) gen["road-tl"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-bl"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-br"].append(tile) for tile in self.tiles["road-br"]: tile = pygame.transform.rotate(tile, 90) gen["road-tr"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-tl"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-bl"].append(tile) for tile in self.tiles["road-bl"]: tile = pygame.transform.rotate(tile, 90) gen["road-br"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-tr"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-tl"].append(tile) # dead-ends for tile in self.tiles["road-bb"]: tile = pygame.transform.rotate(tile, 90) gen["road-rr"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-tt"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-ll"].append(tile) # tees for tile in self.tiles["road--t"]: tile = pygame.transform.rotate(tile, 90) gen["road-|l"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road--b"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-|r"].append(tile) for tile in self.tiles["road-|l"]: tile = pygame.transform.rotate(tile, 90) gen["road--b"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road-|r"].append(tile) tile = pygame.transform.rotate(tile, 90) gen["road--t"].append(tile) for name, tiles in gen.items(): self.tiles[name].extend(tiles)
def get_wall_color(self, cell, dx, dy): return (255, 255, 255) if cell.walls.get((dx, dy)) else (0, 0, 0) if __name__ == '__main__': from sim import Map pygame.init() disp = pygame.display.set_mode((800, 600), DOUBLEBUF) clock = pygame.time.Clock() font = pygame.font.Font(pygame.font.get_default_font(), 16) model = Map() model.load("level1") renderer = Renderer() buf = pygame.Surface(disp.get_size()) while 1: clock.tick(6000) for _ in range(0, 2): x, y = model.update() renderer.draw_one(model, x, y, buf) disp.blit(buf, (0, 0)) disp.blit(fps, (600, 2)) if model.census: cnc = font.render("%.2f / %.2f / %.2f" % (model.census.good, model.census.sick, model.census.dead), True, (255, 255, 255)) disp.blit(cnc, (600, 18)) pygame.display.flip()
class Game (object): text_color = (255, 255, 255) cell_highlight_image = pygame.Surface((GRID_W, GRID_H), pygame.SRCALPHA); cell_highlight_image.fill((0xff, 0xff, 0xff, 0x33), (0, 0, GRID_W, GRID_H)) select_voices = [ "voices/sel1.wav", "voices/sel2.wav", "voices/sel3.wav", "voices/sel4.wav", "voices/sel5.wav", "voices/sel6.wav", ] ack_voices = [ "voices/ack1.wav", "voices/ack2.wav", "voices/ack3.wav", "voices/ack4.wav", ] def __init__(self, m): self.model = m self.units = [unit.Unit(self.model, x, y) for x, y in self.model.conf.units] self.all_effects = pygame.sprite.Group() self.individual_effects = defaultdict(pygame.sprite.Group) self.all_walkers = pygame.sprite.Group() self.renderer = render.Renderer() self.renderer.draw(self.model) self.clock = pygame.time.Clock() # Win conditions self.win_duration_sec = self.model.conf.game["duration"] self.win_duration_frames = self.model.conf.game["duration"] * FRAMES_PER_SECOND self.win_living_min_threshold = self.model.conf.game["living_min_threshold"] self.font = bont.Tiny() self.news_font = data.load_font(*NEWS_FONT) self.over_font = data.load_font(*OVER_FONT) self.selection = None self.last_selection = None self.need_destination = False self.pending_cmd = None self.buttons = buttons.ButtonRegistry() self.buttons.add_sprite_button("Cleanse", self.send_reap, 150, 160) self.buttons.add_sprite_button("Burn", self.send_burn, 150, 180) self.buttons.add_sprite_button("Block", self.send_block, 205, 160) self.buttons.add_sprite_button("Cancel", self.cancel_selection, 205, 180) self.advisor_face = data.load_image("faces/6p.png") self.frame = 0 self.newsflash = None # don't fire a newsflash right away self.next_newsflash = 5 * FRAMES_PER_SECOND self.hover_info = hover_info.HoverInfo() self.over = None self.paused = False self.final_click = False self.voice_chan = None self.select_voice_iter = itertools.cycle(self.select_voices) music.enqueue("minor1") def set_pending_cmd(self, cmd): mouse.set_cursor("target", 160) self.newsflash = newsflash.Unit(cmd[0]) self.need_destination = True self.pending_cmd = cmd def unset_pending_cmd(self): mouse.set_cursor("default") self.need_destination = False self.pending_cmd = None self.buttons.unset_pending_button() self.newsflash = None def execute_pending_cmd(self, dst): x, y = dst self.unblock_cell() self.selection.set_command("move", x, y, self.pending_cmd) data.load_sample(random.choice(self.ack_voices)).play() self.unset_pending_cmd() self.selection = None def send_reap(self): if not self.selection: return self.set_pending_cmd(("reap", self.model.grid)) def send_burn(self): if not self.selection: return self.set_pending_cmd(("burn", self.model.grid)) def send_block(self): if not self.selection: return self.set_pending_cmd(("block", self.model.grid)) def cancel_selection(self): self.selection = None self.unset_pending_cmd() def unblock_cell(self): unit = self.selection unit.is_blocking = False x, y = self.find_cell((unit.x, unit.y)) for u in self.units: if not u.is_blocking: continue ux, uy = self.find_cell((u.x, u.y)) if ux == x and uy == y: return self.model.grid[x, y].unblock() def find_unit(self, pos): for unit in self.units: if unit.rect.collidepoint(pos): return unit return None def find_cell(self, pos): x, y = pos return int(x / self.renderer.stride_x), int(y / self.renderer.stride_y) def handle_click(self, ev): mx, my = ev.pos pos = mx // SCALE_FACTOR, my // SCALE_FACTOR if self.over is not None: self.final_click = True if self.paused: # unpause and finish newsflash if self.newsflash is not None: self.newsflash.show = 1 self.paused = False return if self.selection: if self.buttons.process_click(pos): return if self.need_destination: self.newsflash = None dst = self.find_cell(pos) self.execute_pending_cmd(dst) return if self.selection: self.newsflash = None self.selection = self.find_unit(pos) if self.selection: if not self.voice_chan or not self.voice_chan.get_busy(): if self.selection != self.last_selection: self.select_voice_iter = itertools.cycle(self.select_voices) self.voice_chan = data.load_sample(next(self.select_voice_iter)).play() self.last_selection = self.selection self.newsflash = newsflash.Unit("prompt") else: self.last_selection = None def update_one(self): dx, dy, new_dead, new_sick, caught_fire = self.model.update() cell = self.model.grid[dx, dy] self.individual_effects[dx, dy].update() if new_dead > 0 and cell.view != "field": effects.Ghost(new_dead, dx * GRID_W, dy * GRID_H, self.all_effects, self.individual_effects[dx, dy]) if new_sick > 0: effects.Infection(dx * GRID_W, dy * GRID_H, self.all_effects, self.individual_effects[dx, dy]) if caught_fire: effects.Fire(dx * GRID_W, dy * GRID_H, self.all_effects, self.individual_effects[dx, dy]) flow_dir = max(cell.incoming_acu.keys(), key=lambda d: cell.incoming_acu[d]) if cell.incoming_acu[flow_dir] >= POP_FLOW_ALIVE_NR_TRIGGER: dirx, diry = flow_dir # Shift animation to the cell-origin of the flow dx, dy = dx - dirx, dy - diry # Do not fire up more than one animation effect at the same time if len(self.individual_effects[dx, dy]) == 0 and random.random() <= POP_FLOW_PROBABILiTY_TRIGGER: effects.Walker(dx * GRID_W, dy * GRID_H, dirx, diry, self.all_effects, self.individual_effects[dx, dy]) cell.incoming_acu[flow_dir] = 0.0 def update_music(self): census = self.model.census if census is None: music.update(1.0) return if census.alive > 1.0: music.update(1 - min(1, max(0, census.good / census.alive))) else: music.update(0.0) not_good = census.not_good if census.good > not_good * 3: music.enqueue("minor1") elif census.good > census.sick * 2: music.enqueue("minor2") else: music.enqueue("diminished") def update(self, disp): census = self.model.census self.update_music() if not self.paused and self.over is None: self.frame += 1 self.clock.tick(FRAMES_PER_SECOND) if self.over is None and not self.paused: # let the doc warp to the starting location if self.frame > 1: if len(self.model.conf.messages) > 0: self.play_level_message() if census is not None: # Determine the game outcome: win or defeat if (census.good + census.sick) < self.win_living_min_threshold: self.cancel_selection() self.newsflash = newsflash.Loss(self.win_living_min_threshold, census) self.over = False elif self.frame >= self.win_duration_frames: self.cancel_selection() self.newsflash = newsflash.Victory(census) self.over = True for _ in range(0, UPDATES_PER_FRAME): self.update_one() self.renderer.blit(disp) self.all_effects.draw(disp) self.draw_stats(disp) self.draw_newsflash(disp, census) for unit in self.units: if not self.paused: unit.update() if not self.paused and self.over is None and unit.command[0] == unit.cmd_reap and random.random() > 0.96: effects.Plus(unit.x * GRID_W, unit.y * GRID_H, self.all_effects, self.individual_effects[unit.x, unit.y]) unit.draw(disp, self.selection == unit, self.paused) if self.over is not None: if self.over: music.switch("major") self.draw_game_over(disp, "SUCCESS", TO_NEXT_LEVEL if self.model.conf.next_level else BACK_TO_MENU) if self.final_click: n = self.model.next_level() if n is not None: return Game(n) return title.Title() else: music.switch("diminished") self.draw_game_over(disp, "GAME OVER", BACK_TO_MENU) return self if not self.final_click else title.Title() if self.selection: self.buttons.draw(disp) self.draw_cell_hover(disp) return self def draw_game_over(self, targ, text, subtext): t = self.over_font.render(text, True, self.text_color) w, h = t.get_size() sw, sh = SCREEN_W, SCREEN_H - 40 targ.blit(t, (sw / 2 - w / 2, sh / 2 - h / 2)) w = self.font.get_width(subtext) self.font.render(targ, subtext, (sw / 2 - w / 2, sh / 2 + h)) def draw_text(self, targ, text, pos): self.font.render(targ, text, pos) def draw_stats(self, targ): curr_time = self.frame / FRAMES_PER_SECOND self.time_to_cure = self.win_duration_sec - curr_time self.draw_text(targ, " ttl: %5.0f" % self.time_to_cure, (196, 2)) self.draw_text(targ, "save: %5.0f" % self.win_living_min_threshold, (196, 12)) def draw_population(self, targ, pop): if pop is None: return cnc_text = "%.2f / %.2f / %.2f" % (pop.good, pop.sick, pop.dead) self.draw_text(targ, cnc_text, (2, 160)) def draw_newsflash(self, targ, pop): news = self.newsflash if news is not None: news.advance() news.draw(targ) if news.finished: self.newsflash = None return if self.frame >= self.next_newsflash and pop is not None: self.newsflash = newsflash.Random(pop, self.time_to_cure) self.next_newsflash = self.frame + random.randint(1 * FRAMES_PER_SECOND, 5 * FRAMES_PER_SECOND) def draw_cell_hover(self, targ): (mx, my) = pygame.mouse.get_pos() mpos = mx // SCALE_FACTOR, my // SCALE_FACTOR cpos = self.find_cell(mpos) if cpos not in self.model.grid: return (cx, cy) = cpos[0] * GRID_W, cpos[1] * GRID_H cell = self.model.grid[cpos] highlight_cell = True # TODO This mode may depend on a game state if highlight_cell: targ.blit(self.cell_highlight_image, (cx, cy, 0, 0)) show_cell_stats = True # TODO This mode may depend on a game state if show_cell_stats: x, y = (cx, cy) if STICK_HOVER_INFO_TO_CELL else mpos self.hover_info.draw(x, y, cell.pop, targ) def play_level_message(self): game_time = self.frame / FRAMES_PER_SECOND if not self.newsflash: message = self.model.conf.messages[0] if game_time > int(message["min_time"]): self.model.conf.messages.pop(0) self.paused = True def finished_cb(): self.paused = False self.newsflash = newsflash.LevelMessage(message["face"], message["name"], message["msg"], finished_cb)