def __init__ (self, world, pos, vel, radius = conf.ASTEROID['max radius'], density = conf.ASTEROID['density']): self.world = world GravitySink.__init__(self, pos, vel, density * radius ** 3, radius) self.graphic = Graphic('asteroid.png', (ir(pos[0] - radius), ir(pos[1] - radius))) self.graphic = mk_graphic(self) position_graphic(self)
def _resize (self, sfc, last, w, h, about = (0, 0)): """Backend for resize.""" start_w, start_h = start_sz = sfc.get_size() if w is None: w = start_w if h is None: h = start_h sz = (w, h) about = (about[0], about[1]) if last is not None: last_w, last_h, (last_ax, last_ay) = last if sz == (last_w, last_h) and about == (last_ax, last_ay): # no change to arguments return (None, None, None) if sz == start_sz and about == (0, 0): return (sfc, None, None) scale = (float(w) / start_w, float(h) / start_h) offset = (ir((1 - scale[0]) * about[0]), ir((1 - scale[1]) * about[1])) def apply_fn (g): g._scale = scale x, y, gw, gh = g._rect g._rect = Rect(x + offset[0], y + offset[1], gw, gh) def undo_fn (g): g._scale = (1, 1) x, y, gw, gh = g._rect g._rect = Rect(x - offset[0], y - offset[1], gw, gh) return (self.scale_fn(sfc, sz), apply_fn, undo_fn)
def start (self, target): self.target = target data = self.data methods = data['affects'] self._news_end = data['news end'] # generate news substitution text t = triangular(*data['time']) self._news_data = n_data = { 't': '{0} days'.format(ir(t)), 'r': '{0}-{1} days'.format(data['time'][0], data['time'][2]) } if data['type'] == 'p': p = target.name n_data['p'] = p n_data['P'] = p.capitalize() t_pos = target.pos elif data['type'] == 'c': t_pos = target.centre # and put action into effect else: # data['type'] == 'a' n_data['a'] = target[0] for obj in target[1] + target[2]: # people and connections obj.disable_methods(self, *methods) if data['type'] != 'a': n_data['a'] = self._wmap.area(t_pos) target.disable_methods(self, *methods) # generate initial news news = self._mk_news(data['news start']) return (ir(t * conf.DAY_FRAMES), data['cost'], news)
def draw (self, screen): x, y = self.body.position c = (255, 0, 0) if isinstance(self, Player) else (0, 150, 0) pg.draw.circle(screen, c, (ir(x), ir(y)), self.radius) a = self.angle length = 1.5 * self.radius end = (x + length * cos(a), y + length * sin(a)) pg.draw.line(screen, (0, 0, 255), (x, y), end)
def update_scale (self): # determine display pos w, h = self.size sw, sh = conf.RES self.scale = s = min(sw / float(w), sh / float(h)) w *= s h *= s self.pos = (ir((sw - w) / 2), ir((sh - h) / 2)) self.rect = pg.Rect(self.pos, (ir(w), ir(h)))
def to_screen (self, r): l, t = self.pos s = self.scale x = ir(l + s * r[0]) y = ir(t + s * r[1]) if len(r) == 2: return (x, y) else: return (x, y, ir(r[2] * s), ir(r[3] * s))
def draw_pos (self, screen, pos = (0, 0)): if self.sending: x0, y0 = start = self.sending.pos x1, y1 = end = self.other().pos r = self.progress pos = sum_pos(pos, (ir(x0 + r * (x1 - x0)), ir(y0 + r * (y1 - y0))), self._offset) screen.blit(self._pos_img, pos)
def img (self, filename, size = None, cache = True): """Load or scale an image, or retrieve it from cache. img(filename[, size], cache = True) -> surface :arg filename: a filename to load. :arg size: scale the image. Can be an ``(x, y)`` size, a rect (in which case its dimensions are used), or a number to scale by. If ``(x, y)``, either ``x`` or ``y`` can be ``None`` to scale to the other with aspect ratio preserved. :arg cache: whether to store this image in/retrieve it from the appropriate cache if possible. :rtype: ``pygame.Surface`` """ # get standardised cache key if size is not None: if isinstance(size, (int, float)): size = float(size) else: if len(size) == 4: # rect size = size[2:] size = tuple(size) key = (filename, size) if key in self.img_cache: return self.img_cache[key] # else new: load/render filename = conf.IMG_DIR + filename # also cache loaded images to reduce file I/O if cache and filename in self.file_cache: img = self.file_cache[filename] else: img = convert_sfc(pg.image.load(filename)) if cache: self.file_cache[filename] = img # scale if size is not None and size != 1: current_size = img.get_size() if not isinstance(size, tuple): size = (ir(size * current_size[0]), ir(size * current_size[1])) # handle None for i in (0, 1): if size[i] is None: size = list(size) scale = float(size[not i]) / current_size[not i] size[i] = ir(current_size[i] * scale) img = pg.transform.smoothscale(img, size) # speed up blitting (if not resized, this is already done) img = convert_sfc(img) if cache: # add to cache (if not resized, this is in the file cache) self.img_cache[key] = img return img
def print_l (*args): if not self.l: return x = min(x[0] for x in self.l) y = min(x[1] for x in self.l) w = max(x[0] for x in self.l) - x h = max(x[1] for x in self.l) - y x = ir(float(x - self.pos[0]) / self.scale) y = ir(float(y - self.pos[1]) / self.scale) w = ir(float(w) / self.scale) h = ir(float(h) / self.scale) print (x, y, w, h)
def rescale (self, w = 1, h = 1, about = (0, 0)): """A convenience wrapper around resize to scale by a ratio. rescale(w = 1, h = 1, about = (0, 0)) -> self :arg w: the new width; ratio of the width before scaling. :arg h: the new height; ratio of the height before scaling. :arg about: the ``(x, y)`` position relative to the top-left of the graphic to scale about. """ ow, oh = self.sfc_before_transform('resize').get_size() return self.resize(ir(w * ow), ir(h * oh), about)
def update(self): fps = self.level.game.scheduler.timer.fps speed = ir(float(conf.CAR_SPEED) / fps) speed_jitter = float(conf.CAR_SPEED_JITTER) / fps for lane, (lane_mode, stopping, (dirn, cars)) in \ enumerate(zip(self._modes, self._stopping, self.cars)): # remove OoB cars if cars and self.oob(dirn, cars[0].rect): cars.pop(0) # add cars if needed if not cars or self.needs_car(dirn, cars[-1].rect): self.add_car(lane) # move cars prev = None new_mode = current_mode = 'moving' for car in cars: if lane_mode == 'moving': car.start() if car.mode == 'moving': r = car.rect front = car.front(dirn) v = speed + max(1 - speed, randsgn() * ir(expo(1 / speed_jitter))) stop = front + dirn * v # stop before stop marker if lane_mode != 'moving' and current_mode == 'moving': if dirn * front <= dirn * stopping: stop = stopping new_mode = lane_mode # stop before next car if prev is not None: this_stop = prev.back(dirn) - \ dirn * conf.CAR_GAP[current_mode] stop = dirn * min(dirn * stop, dirn * this_stop) new_v = dirn * (stop - front) if new_v < v: # velocity reduced v = max(0, new_v) if v == 0 and \ (current_mode == 'moving' or prev.mode != 'moving'): # to make sure we only stop if every car in front # has stopped - in case this car is faster than the # one in front car.set_mode(new_mode) current_mode = new_mode r.move_ip(v * dirn, 0) else: new_mode = current_mode = car.mode prev = car
def update (self): fps = self.level.game.scheduler.timer.fps speed = ir(float(conf.CAR_SPEED) / fps) speed_jitter = float(conf.CAR_SPEED_JITTER) / fps for lane, (lane_mode, stopping, (dirn, cars)) in \ enumerate(zip(self._modes, self._stopping, self.cars)): # remove OoB cars if cars and self.oob(dirn, cars[0].rect): cars.pop(0) # add cars if needed if not cars or self.needs_car(dirn, cars[-1].rect): self.add_car(lane) # move cars prev = None new_mode = current_mode = 'moving' for car in cars: if lane_mode == 'moving': car.start() if car.mode == 'moving': r = car.rect front = car.front(dirn) v = speed + max(1 - speed, randsgn() * ir(expo(1 / speed_jitter))) stop = front + dirn * v # stop before stop marker if lane_mode != 'moving' and current_mode == 'moving': if dirn * front <= dirn * stopping: stop = stopping new_mode = lane_mode # stop before next car if prev is not None: this_stop = prev.back(dirn) - \ dirn * conf.CAR_GAP[current_mode] stop = dirn * min(dirn * stop, dirn * this_stop) new_v = dirn * (stop - front) if new_v < v: # velocity reduced v = max(0, new_v) if v == 0 and \ (current_mode == 'moving' or prev.mode != 'moving'): # to make sure we only stop if every car in front # has stopped - in case this car is faster than the # one in front car.set_mode(new_mode) current_mode = new_mode r.move_ip(v * dirn, 0) else: new_mode = current_mode = car.mode prev = car
def __init__ (self, level, people, methods): self.people = people self.dist = dist = people[0].dist(people[1]) - 2 * conf.PERSON_RADIUS x1, y1 = people[0].pos x2, y2 = people[1].pos self.centre = (ir(.5 * (x1 + x2)), ir(5 * (y1 + y2))) methods = reversed(sorted((method_speed(m, dist), m) for m in methods)) self.methods = OrderedDict((m, {'disabled': [], 'speed': s}) for s, m in methods) self.sending = False self.sent = False self.selected = False self._pos_img = level.game.img('connection-progress.png') w, h = self._pos_img.get_size() self._offset = (-w / 2, -h / 2)
def _jitter_clamp (self, x, lb, ub, lane): s = conf.TILE_SIZE[1] lb = s * lb + 1 ub = s * (ub + 1) - 1 inv_mean = 1. / conf.CRASH_POS_JITTER x = min(max(ir(x + randsgn() * expo(inv_mean)), lb), ub) tile_x = ((x - 1) if lane < 2 else x) / s return (tile_x, x)
def _jitter_clamp(self, x, lb, ub, lane): s = conf.TILE_SIZE[1] lb = s * lb + 1 ub = s * (ub + 1) - 1 inv_mean = 1. / conf.CRASH_POS_JITTER x = min(max(ir(x + randsgn() * expo(inv_mean)), lb), ub) tile_x = ((x - 1) if lane < 2 else x) / s return (tile_x, x)
def vc_alpha(alpha, V, i1, i2, t0, d, tf): """ Calculates the limit value for selling a title, given a percentage 'alpha' """ hoje = datetime.date.today() Nhoje = (hoje - t0).days N = workdays.networkdays(t0, tf) Nc = (tf - t0).days proxInv = hoje + datetime.timedelta(days=d) Nprox = workdays.networkdays(proxInv, tf) Nproxc = (tf - proxInv).days print "\nN: {0:4d}, N Corr: {1:4d}, N Hoje: {2:4d}, " \ "N Prox: {3:4d}, N Prox Corr: {4:4d}\n".format( N, Nc, Nhoje, Nprox, Nproxc) i1d = math.pow(1.0 + i1, 1.0 / util.DIASANO) - 1.0 i2d = math.pow(1.0 + i2, 1.0 / util.DIASANO) - 1.0 t1 = util.ir(Nc) + (1.0 - util.ir(Nc)) * math.pow(1.0 + i1d, N) t2 = util.ir(Nproxc) + (1.0 - util.ir(Nproxc)) * math.pow(1.0 + i2d, Nprox) vLim = (alpha * t1 / (t2 * (1.0 - util.ir(Nhoje))) - util.ir(Nhoje) / (1.0 - util.ir(Nhoje))) * V return vLim
def mk_graphic (obj): graphic = Graphic(obj.img_ident + '.png', layer = conf.GRAPHICS_LAYERS[obj.ident]) w = graphic.w offset = conf.IMG_OFFSETS[obj.img_ident] scale = 2 * float(obj.collision_radius) / (w - 2 * offset) graphic.rescale_both(scale) obj.img_offset = ir(scale * offset) return graphic
def pre_draw (self): ox, oy = conf.PLAYER_OFFSET x, y = (ir(self.rect[0]) + ox, ir(self.rect[1]) + oy) w0, h0 = self.img_size # copy images to use to sfc sfc = self.sfc dirn = self.dirn skew = ir(self.skew) skew = (1 if skew > 0 else -1) * min(abs(skew), conf.PLAYER_MAX_SKEW) blinking = self.blinking if skew != self.last_skew or dirn != self.last_dirn \ or (blinking < 0) != self.last_blinking: sfc.fill((0, 0, 0, 0)) x0, y0 = w0 * abs(skew), h0 if skew > 0 else 0 sfc.blit(self.img, (0, 0), (x0, y0, w0, h0)) f_img = self.f_imgs[blinking < 0][dirn] if dirn: # facing right x0 = w0 * (conf.PLAYER_MAX_SKEW - abs(skew)) # use opposite skew y0 = 0 if skew > 0 else h0 sfc.blit(f_img, (0, 0), (x0, y0, w0, h0)) self.last_scale = None self.last_skew = skew self.last_dirn = dirn self.last_blinking = blinking < 0 # scale x0, y0, x1, y1 = self.squash w = w0 - x0 - x1 h = h0 - y0 - y1 # constrain scale factor mn, mx = conf.PLAYER_MIN_SQUASH, conf.PLAYER_MAX_SQUASH wb = min(max(w, mn * w0), mx * w0) hb = min(max(h, mn * h0), mx * h0) # adjust blit location if constrained if wb != w: assert x0 + x1 != 0 x0 -= (wb - w) * x0 / (x0 + x1) if hb != h: assert y0 + y1 != 0 y0 -= (hb - h) * y0 / (y0 + y1) self.rect_img = pg.Rect(x + ir(x0), y + ir(y0), ir(wb), ir(hb)) # star sound c = self.level.star_channel if c is not None: x0, y0 = self.rect_img.center vs = [] for s in self.level.stars: if not s.got: x1, y1 = s.rect.center v = (abs(x1 - x0) + abs(y1 - y0)) ** 1.5 vs.append(1. / max(conf.STAR_SND_CUTOFF, v)) if vs: v = conf.VOL_MUL * conf.SOUND_VOLUME * conf.SOUND_VOLUMES.get('star', 1) * max(0, *vs) c.set_volume(min(v, 1)) else: c.pause()
def shoot (self, *args): if self.cooldown <= 0: w = self.weapon_data if self.aim_scheme == 'move': a = self.target_angle else: a = self.angle acc = self.shoot_acc * w['acc'] pos = self.body.position args = (w['range'] * self.shoot_range, w['damage'] * self.damage, w['kb'] * self.knockback, self) shoot = self.level.shoot # decide number of times to shoot cooldown = self.max_cooldown * w['cooldown'] self.cooldown = ir(cooldown) frame = self.level.game.scheduler.timer.frame at_once = frame / cooldown + conf.BULLETS_AT_ONCE * w['at_once'] for i in xrange(max(1, ir(at_once))): da = randsgn() * ev(acc) shoot(pos, a + da, *args)
def set_img (self, img = None): if img is None: img = self.__class__.__name__.lower() if isinstance(img, basestring): img = ('obj', img + '.png') self.img = self.level.game.img(img) else: self.img = img self._offset = [ir(float(t_s - i_s) / 2) for t_s, i_s in zip(conf.TILE_SIZE, self.img.get_size())] if not self.held and self.pos is not None: self.level.change_tile(self.pos)
def _rotate (self, sfc, last, angle, about): """Backend for rotate.""" w_old, h_old = sfc.get_size() cx, cy = w_old / 2., h_old / 2. about = (cx, cy) if about is None else (about[0], about[1]) if last is not None: last_angle, last_about = last last_about = (cx, cy) if last_about is None \ else (last_about[0], last_about[1]) if abs(angle - last_angle) < self.rotate_threshold and about == last_about: # no change to arguments return (None, None, None) if abs(angle) < self.rotate_threshold: return (sfc, None, None) # if not already alpha, convert to alpha if sfc.get_alpha() is None and sfc.get_colorkey() is None: sfc = sfc.convert_alpha() new_sfc = self.rotate_fn(sfc, angle) # compute draw offset w_new, h_new = new_sfc.get_size() # v = c_new - about vx = cx - about[0] vy = cy - about[1] # c - about_new = v.rotate(angle) s = sin(angle) c = cos(angle) ax_new = w_new / 2. - (c * vx + s * vy) ay_new = h_new / 2. - (-s * vx + c * vy) # about = offset + about_new offset = (ir(about[0] - ax_new), ir(about[1] - ay_new)) def apply_fn (g): g._angle = angle g._rot_offset = offset def undo_fn (g): g._angle = 0 g._rot_offset = (0, 0) return (new_sfc, apply_fn, undo_fn)
def __init__ (self, level, pos): self.level = level w, h = level.game.img('player.png').get_size() self.img_size = (w / (conf.PLAYER_MAX_SKEW + 1), h / 2) self.rect = list(pos) + list(conf.PLAYER_SIZE) self.old_rect = list(self.rect) ox, oy = conf.PLAYER_OFFSET p = (ir(self.rect[0]) + ox, ir(self.rect[1]) + oy) self.old_rect_img = self.rect_img = Rect(p, self.img_size) self.vel = [0, 0] self.on_ground = 0 self.can_jump = level.ID in conf.CAN_JUMP self.jumping = 0 self.jumped = False self.can_move = level.ID in conf.CAN_MOVE self.moving = False self.moved = False if level.move_channel is not None: level.move_channel.pause() self.img = level.game.img('player.png') self.f_imgs = [level.game.img('player-features.png'), level.game.img('player-features-blinking.png')] self.f_imgs = [(img, pg.transform.flip(img, True, False)) for img in self.f_imgs] self.sfc = pg.Surface(self.img_size).convert_alpha() self.dirn = True self.last_dirn = None self.skew_v = 0 self.skew = 0 self.last_skew = None self.squash_v = [0, 0, 0, 0] self.squash = [0, 0, 0, 0] self.last_scale = None self.blinking = -1 self.last_blinking = None self.to_move = 0
def draw (self, screen): if self.dirty: # draw t = conf.PAUSE_FADE_TIME - self.fade_counter alpha = conf.PAUSE_FADE_RATE * float(t) / conf.PAUSE_FADE_TIME alpha = min(255, ir(alpha)) self.fade_sfc.fill((0, 0, 0, alpha)) for sfc in [self.sfc, self.fade_sfc] + self.texts: screen.blit(sfc, (0, 0)) # update counter self.fade_counter -= 1 if self.fade_counter <= 0: self.dirty = False del self.fade_sfc return True else: return False
def draw (self, screen, offset): r = self.rect.move(offset) screen.blit(self.bg, r) sfc = self.sfc g = self.glow # draw fg with opacity level from glow sfc.fill((255, 255, 255, ir(g * 255))) sfc.blit(self.fg, (0, 0), None, pg.BLEND_RGBA_MULT) screen.blit(sfc, r) # update glow d = self.glow_dirn g += d * conf.STAR_PULSE_SPEED gb = min(1, max(0, g)) if g != gb: d *= -1 self.glow = gb self.glow_dirn = d
def add_con (p1, p2): # need to add dist to self.dists before creating Connection key = frozenset((p1, p2)) used_dists[key] = dists[key] # choose method types this_methods = set() for i in xrange(ir(max(1, n_methods()))): this_methods.add(weighted_rand(methods)) # create connection and add to stores c = Connection(level, (p1, p2), this_methods) self.cons.append(c) p1.cons.append(c) p2.cons.append(c) g1 = groups[p1] g2 = groups[p2] # merge groups g1.update(g2) for p, g in groups.iteritems(): if g is g2: groups[p] = g1
def align (self, pos = 0, pad = 0, offset = 0, rect = None): """Position this graphic within a rect. align(pos = 0, pad = 0, offset = 0, rect = self.manager.surface.get_rect()) -> self :arg pos: ``(x, y)`` alignment; each is ``< 0`` for left-aligned, ``0`` for centred, ``> 0`` for right-aligned. Can be just one number to use on both axes. :arg pad: ``(x, y)`` padding to leave around the inner edge of ``rect``. Can be negative to allow positioning outside of ``rect``, and can be just one number to use on both axes. :arg offset: ``(x, y)`` amounts to offset by after all other positioning; can be just one number to use on both axes. :arg rect: Pygame-style rect to align in. """ pos = [pos, pos] if isinstance(pos, (int, float)) else list(pos) if isinstance(pad, (int, float)): pad = (pad, pad) if isinstance(offset, (int, float)): offset = (offset, offset) if rect is None: rect = self._manager.surface.get_rect() else: rect = Rect(rect) rect = rect.inflate(-2 * pad[0], -2 * pad[1]) sz = self._rect[2:] for axis in (0, 1): align = pos[axis] if align < 0: x = 0 elif align == 0: x = (rect[2 + axis] - sz[axis]) / 2. else: # align > 0 x = rect[2 + axis] - sz[axis] pos[axis] = ir(rect[axis] + x + offset[axis]) self.rect = (pos, sz) return self
def __init__ (self, level, size, selected): Widget.__init__(self, size) self.level = level self.selected = selected self.selecting = None self._sel_area = None self._actions = [] self._news = [] self.areas = areas = {} w, h = self.size # generate areas names = sample(conf.AREAS, conf.NUM_AREAS) n_rows = int(ceil(len(names) ** .5)) n_per_row = float(len(names)) / n_rows done_f = done = 0 dy = ir(h / float(n_rows)) y = dy / 2 for j in xrange(n_rows): done_f += n_per_row n_this_row = int(done_f) - done dx = ir(w / float(n_this_row)) x = dx / 2 for i in xrange(n_this_row): areas[names[done + i]] = (x, y) x += dx done += n_this_row y += dy # generate people self.people = ps = [] dists = {} self.dists = used_dists = {} b = conf.WMAP_BORDER x0 = y0 = b x1 = w - b y1 = h - b nearest = 2 * conf.PERSON_RADIUS + conf.PERSON_NEAREST for i in range(conf.NUM_PEOPLE): while True: x, y = randint(x0, x1), randint(y0, y1) this_dists = {} for p in ps: ox, oy = p.pos dist = ((ox - x) * (ox - x) + (oy - y) * (oy - y)) ** .5 if dist < nearest: break this_dists[p] = dist else: new_p = Person(level, self, (x, y)) for p, dist in this_dists.iteritems(): dists[frozenset((new_p, p))] = dist ps.append(new_p) break # compute all remaining distances for p1 in ps: for p2 in ps: if p1 is not p2: key = frozenset((p1, p2)) if key not in dists: x1, y1 = p1.pos x2, y2 = p2.pos dists[key] = ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** .5 def add_con (p1, p2): # need to add dist to self.dists before creating Connection key = frozenset((p1, p2)) used_dists[key] = dists[key] # choose method types this_methods = set() for i in xrange(ir(max(1, n_methods()))): this_methods.add(weighted_rand(methods)) # create connection and add to stores c = Connection(level, (p1, p2), this_methods) self.cons.append(c) p1.cons.append(c) p2.cons.append(c) g1 = groups[p1] g2 = groups[p2] # merge groups g1.update(g2) for p, g in groups.iteritems(): if g is g2: groups[p] = g1 # generate connections methods = dict((method, data['freq']) for method, data in conf.METHODS.iteritems()) n_methods = conf.METHODS_PER_CON self.cons = [] # and group by whether connected groups = dict((p, set((p,))) for p in ps) n_cons = conf.CONS_PER_PERSON max_cons = conf.MAX_CONS_PER_PERSON # give everyone connections biased towards people near them for p in ps: # distances have a non-zero minimum others = dict((other, 1. / dists[frozenset((p, other))] ** \ conf.SHORT_CONNECTION_BIAS) for other in ps if other is not p) for c in p.cons: del others[c.other(p)] targets = [] this_n_cons = ir(max(1, min(max_cons, min(len(others), n_cons())))) for i in xrange(this_n_cons - len(p.cons)): other = weighted_rand(others) targets.append(other) del others[other] for other in targets: add_con(p, other) # reduce to one group by adding extra connections frozen_groups = set(frozenset(g) for g in groups.itervalues()) while len(frozen_groups) > 1: i = iter(frozen_groups) g1 = next(i) g2 = next(i) dist, p1, p2 = min(min((dists[frozenset((p1, p2))], p1, p2) for p2 in g2 if p2 is not p1) for p1 in g1) add_con(p1, p2) frozen_groups = set(frozenset(g) for g in groups.itervalues()) # give some people full names ps = ps[:min(conf.NUM_FULL_NAMES, len(ps))] for p, name in zip(ps, sample(conf.FULL_NAMES, len(ps))): p.name = name # let someone know self.n_know = 0 ps[0].recieve()
def position_graphic (obj): o = obj.img_offset p = obj.pos r = obj.collision_radius obj.graphic.pos = (ir(p[0] - r - o), ir(p[1] - r - o))
def _move(self, dest): self._face(dest) self.pos = dest return ir(conf.FROG_MOVE_TIME * self.level.game.scheduler.timer.fps)
def round_val (do, v): return ir(v) if isinstance(v, (int, float)) and do else v
def __init__ (self, game, event_handler): self.game = game # input event_handler.add_event_handlers({ pg.MOUSEBUTTONDOWN: self.click, pg.MOUSEMOTION: self.set_current_from_mouse }) event_handler.add_key_handlers([ (conf.KEYS_BACK, self.quit, eh.MODE_ONDOWN), (conf.KEYS_NEXT, self.select, eh.MODE_ONDOWN), ] + [ (ks, [(self.move, (i,))], eh.MODE_ONDOWN_REPEAT, 15, 7) for i, ks in enumerate(conf.KEYS_MOVE) ]) game.linear_fade(*conf.LS_FADE_IN) # generate unlocked list unlocked = [] n_stars = sum(len(lvl.get('stars', [])) for lvl in conf.LEVELS) got_stars = 0 for ID, i in conf.STARS: if len(conf.LEVELS) > ID and len(conf.LEVELS[ID].get('stars', [])) > i: got_stars += 1 secret = [i for i in xrange(len(conf.LEVELS)) if i not in conf.EXISTS] require = split(n_stars, len(secret)) req = 0 for ID, this_req in zip(secret, require): req += this_req if got_stars >= req: unlocked.append(ID) if conf.DEBUG: print '{0}/{1} stars; need {2}'.format(got_stars, n_stars, require) # generate level thumbnails ids = set(conf.EXISTS + unlocked) self.num_levels = n = len(ids) self.cols = cols = min(i for i in xrange(n + 1) if i * i >= n) self.rows = rows = n / cols + bool(n % cols) self.levels = levels = [] self.level_ids = level_ids = {} w, h = conf.RES ws = split(w, cols) hs = split(ir(h * float(rows) / cols), rows) x = w_i = h_i = 0 y = (h - sum(hs)) / 2 draw_sfc = pg.Surface(conf.RES) vertical_order = [] row = [] vertical_order.append(row) for j, i in enumerate(ids): row.append(i) rect = pg.Rect((x, y, ws[w_i], hs[h_i])).inflate(-2, -2) # generate image sfc = pg.Surface(rect[2:]) l = level.Level(game, None, i) l.draw(draw_sfc) sfc = pg.transform.smoothscale(draw_sfc, rect[2:]) # dim or brighten surface if i in conf.COMPLETED_LEVELS: mod_sfc = pg.Surface(rect[2:]).convert_alpha() mod_sfc.fill(conf.LS_WON_OVERLAY) sfc.blit(mod_sfc, (0, 0)) level_ids[i] = j levels.append((i, rect, sfc.convert())) # get next rect x += ws[w_i] w_i += 1 if w_i == cols: row = [] vertical_order.append(row) x = w_i = 0 y += hs[h_i] h_i += 1 self.last = 0 self.last_current = self.current = None self.changed = False self.set_current_from_mouse() self.vertical_order = sum([[row[i] for row in vertical_order if len(row) > i] for i in xrange(cols)], []) self.finished = False
def show (self, obj, action = None): """Show something. show(obj, action = None) obj: the thing to show: None for nothing, a Person, a Connection, or name for an area. If action is given, area is (name, people, cons), where people and cons are those inside the area. action: if the player is selecting an area for an action, this is the Action instance. """ # store what we're showing if obj is None: showing_type = '' elif isinstance(obj, wmap.Connection): showing_type = 'c' elif isinstance(obj, wmap.Person): showing_type = 'p' else: # area showing_type = 'a' if hasattr(self, 'showing') and obj == self.showing and \ showing_type == self.showing_type and action == self.action: return # update things' selected states if not hasattr(self, 'showing') or obj != self.showing: if hasattr(self, 'showing'): if self.showing_type in ('p', 'c'): self.showing.unselect() elif self.action and self.showing_type == 'a': self.wmap.unsel_area() if showing_type in ('p', 'c'): obj.select() elif action and showing_type == 'a': self.wmap.sel_area(action.pos, action.data['radius']) self.showing = obj self.showing_type = showing_type self.action = action # data is a list of rows, each a list of widget representations. # Widgets are vertically centred within rows. Representations are # (font, text, width), a surface, a Widget, or an int for padding. A # row can also be an int for padding. width = self.size[0] data = [] if obj is None: head = 'Nothing selected' elif showing_type == 'c': current_method = obj.current_method if obj.sending else None affected = action.data['affects'] if action else [] head = 'Selected: connection' data.append(5) data += self._method_map( [(method, data['disabled'], method == current_method, method in affected) for method, data in obj.methods.iteritems()] ) data += [ 5, (('normal', '{0} km long'.format(ir(obj.dist)), None),) ] elif showing_type == 'p': head = 'Selected: {0}'.format(obj.name) # icon img = obj.img(False) img_w = img.get_width() data += [5, (img,)] else: # area if action: name, people, cons = obj else: name = obj head = 'Selected: {0}'.format(name) if action: body_text = 'contains {0} people, {1} connections' data += [ 5, (('normal', body_text.format(len(people), len(cons)), width),) ] if action: # insert action note if obj is None: text = 'Select {0} for the action \'{1}\'.' text = text.format({ 'p': 'a person', 'c': 'a connection', 'a': 'an area' }[action.type], action.data['desc']) else: text = '(For the action \'{0}\'.)'.format(action.data['desc']) data = [5, (('normal', text, width),)] + data if obj is None: methods = [(method, False, False, True) for method in action.data['affects']] data = [data[0]] + self._method_map(methods) + data[1:] # add heading data = [5, (('subhead', head, width),)] + data if action: # add buttons data.append(10) if obj is not None: data += [ (ui.Button(width, 'OK', self._start),), 5 ] data.append((ui.Button(width, 'Cancel', self._cancel),)) # generate widgets ws = [] y = 0 for row in data: if isinstance(row, int): y += row continue this_ws = [] hs = [] # create widgets and position in x x = 0 for w in row: if isinstance(w, pg.Surface): w = ui.Img(w) elif isinstance(w, int): # padding x += w continue elif isinstance(w, ui.Widget): pass else: # text font, text, ww = w just = 0 if font == 'normal' else 1 w = ui.Text((ww, None), text, font, just) w.bg = self._bg this_ws.append((x, w)) x += w.size[0] hs.append(w.size[1]) # centre in the row h = max(hs) for this_h, (x, w) in zip(hs, this_ws): ws.append(((x, y + (h - this_h) / 2), w)) y += h self.widgets = ws self.dirty = True