def on_button_pressed(self, ev: ButtonPressed, signal) -> bool: d = (self.position - ev.position).length if d < 1.0: self.smooshed = True self.smoosh_time = 0.0 self.pressed_time = get_time() return d < 1.0
def on_pre_render(self, update, signal): t = get_time() d = update.time_delta self.last_frame = t clear = [] for i, tween in enumerate(self.tweens): if tween.start_time > t: continue if tween.start_value is None: tween.start_value = getattr(tween.obj, tween.attr) tr = (t - tween.start_time) / (tween.end_time - tween.start_time) tr = min(1.0, max(0.0, tr)) tr = EASING[tween.easing](tr) value = lerp(tween.start_value, tween.end_value, tr) setattr(tween.obj, tween.attr, value) if tr >= 1.0: clear.append(i) for i in reversed(clear): del self.tweens[i] if not self.is_tweening: callbacks = self.callbacks[:] self.callbacks.clear() for func in callbacks: func()
def on_idle(self, idle_event: events.Idle, signal): t = get_time() if t >= self.target_clock: signal(events.PreRender(t - self.last_frame)) signal(events.Render()) self.target_clock = t + self.target_frame_length self.last_frame = t
def debounce(obj, marker, delay): v = getattr(obj, marker, 0.0) t = get_time() if v + delay < t: setattr(obj, marker, t) return True else: return False
def __init__(self, resolution=DEFAULT_RESOLUTION, window_title: str = "PursuedPyBear", target_frame_rate: int = 30, target_camera_width=25, **kwargs): self.resolution = resolution self.window = None self.window_title = window_title self.scene_cameras = {} self.target_camera_width = target_camera_width self.target_frame_rate = target_frame_rate self.target_frame_length = 1 / self.target_frame_rate self.target_clock = get_time() + self.target_frame_length self.last_frame = get_time() self._texture_cache = ObjectSideData()
def on_pre_render(self, ev, signal): self.layer = pos_to_layer(self.position) t = get_time() if self.absorbing > t or self.growing: s = (cos((self.absorbing - t + 1.0) * pi) + 1.0) * 0.5 + 0.5 o = round(64 * s) self.root.opacity = o elif self.root.opacity > 0: self.root.opacity -= 1
def on_button_pressed(self, ev: ButtonPressed, signal): clicked = super().on_button_pressed(ev, signal) if clicked: # TODO: This is pretty cludging and won't always match the visual clouds self.cloud_radius = 0.5 self.cloud_id = int(get_time() * 1000) tweening.tween(self, "cloud_radius", C.SMOOSHROOM_CLOUD_RADIUS_MAX, C.SMOOSHROOM_CLOUD_RADIUS_TIME, easing='quad_out')
def test_failer_timed(): failer = testutil.Failer(fail=lambda e: False, message="Should time out", run_time=0.1, engine=None) start_time = get_time() while True: try: failer.on_idle(Idle(0.0), lambda x: None) except AssertionError as e: if e.args[0] == "Test ran too long.": end_time = get_time() break else: raise run_time = end_time - start_time assert abs(run_time - 0.1) <= 0.011
def start(self): """ Starts the engine. Called by :meth:`GameEngine.run` before :meth:`GameEngine.main_loop`. You shouldn't call this yourself unless you're embedding :mod:`ppb` in another event loop. """ self.running = True self._last_idle_time = get_time() self.activate({"scene_class": self.first_scene, "kwargs": self.scene_kwargs})
def on_viking_death(self, ev, signal): v = ev.viking.position d = (self.position - v).length if d < self.absorb_radius and not ev.claimed: self.absorbing = get_time() + 5.0 signal(ParticleSpawnerCreate( source=(v + ppb.Vector(-1, -0.4), v), dest=(v + ppb.Vector(-0.9, 1.25), v + ppb.Vector(-0.1, 1)), color=COLOR['YELLOW'], lifespan=5.0, )) delay(5.0, lambda: signal(ScorePoints(1))) delay(5.0, lambda: signal(CreateFloatingNumber(1, ev.viking.position + ppb.Vector(0, 1), COLOR['YELLOW']))) ev.claimed = True
def on_update(self, ev: Update, signal): # If the mushroom is being smooshed and it has toxins to squish... if self.toxins and self.smooshed: # Set smoosh frame t = tweening.EASING['quad_out'](self.smoosh_time) assert 0.0 <= t <= 1.0 i = tweening.lerp(0, 3, t) self.image = MUSHROOM_SPRITES[i] # Accumulate cloud counter from toxin counter self.smoosh_time = min(1.0, self.smoosh_time + ev.time_delta) self.toxins = max(0.0, self.toxins - ev.time_delta) signal(MeterUpdate(self, 'toxins', self.toxins)) self.cloud = self.cloud + ev.time_delta # If the cloud accumulator reaches threashold, # emit clouds and clear the accumulator. if self.cloud >= C.SMOOSHROOM_TOXIN_DMG_RATE: signal(EmitCloud(self.position, self.cloud_id)) self.pressed_time = get_time() self.cloud -= C.SMOOSHROOM_TOXIN_DMG_RATE for viking in ev.scene.get(tag='viking'): d = (viking.position - self.position).length if d <= self.cloud_radius + 0.5: viking.on_mushroom_attack( MushroomAttack(get_time(), viking, scene=ev.scene), signal) # If the mushroom isn't being smooshed, increase toxin accumulator # and reset the cloud accumulator. elif not self.smooshed and self.toxins < 1.0: self.toxins = min( 1.0, self.toxins + ev.time_delta * C.SMOOSHROOM_TOXIN_CHARGE) self.cloud = 0.0 signal(MeterUpdate(self, 'toxins', self.toxins))
def loop_once(self): """ Iterate once. If you're embedding :mod:`ppb` in an external event loop call once per loop. """ if not self.entered: raise ValueError("Cannot run before things have started", self.entered) now = get_time() self.signal(events.Idle(now - self._last_idle_time)) self._last_idle_time = now while self.events: self.publish()
def on_viking_attack(self, ev, signal): super().on_viking_attack(ev, signal) if ev.target is self: self.smooshed = True self.cloud += C.SMOOSHROOM_TOXIN_DMG_RATE self.toxins -= C.SMOOSHROOM_TOXIN_DMG_RATE self.cloud_radius = 0.5 self.cloud_id = int(get_time() * 1000) tweening.tween(self, "cloud_radius", C.SMOOSHROOM_CLOUD_RADIUS_MAX, C.SMOOSHROOM_CLOUD_RADIUS_TIME, easing='quad_out') delay(0.25, lambda: setattr(self, 'smooshed', False))
def on_idle(cls, idle, signal): clear = [] for t in list(cls.timers): if t.clear: clear.append(t) else: now = get_time() if now >= t.end_time: if t.until is None or t.until > now: t.callback() if t.repeating > 0: if t.until is None or t.until > now: t.end_time += t.repeating continue clear.append(t) for t in clear: cls.timers.remove(t)
def start(self): """ Starts the engine. Called by :meth:`GameEngine.run` before :meth:`GameEngine.main_loop`. You shouldn't call this yourself unless you're embedding :mod:`ppb` in another event loop. """ self.running = True self._last_idle_time = get_time() if isinstance(self.first_scene, type): scene = self.first_scene(**self.scene_kwargs) else: scene = self.first_scene self._start_scene(scene, None)
def tween(self, entity, attr, *args, **kwargs): if len(args) == 2: end_value, duration = args start_value = getattr(entity, attr) elif len(args) == 3: start_value, end_value, duration = args delay = kwargs.pop('delay', 0) self.used = True start_time = get_time() + delay self.tweens.append( Tween( start_time=start_time, end_time=start_time + duration, obj=entity, attr=attr, start_value=start_value, end_value=end_value, **kwargs, ))
def delay(cls, seconds, func): t = Timer(get_time() + seconds, func) cls.timers.add(t) return t
def on_update(self, ev: Update, signal): self.state.on_update(self, ev, signal) if self.hp and 'shield' in self.parts: s = math.sin(get_time() * 4) self.parts['shield'].position = ppb.Vector(0.5, 0.05 - s * 0.1)
def __init__(self, name=None): self.name = name or f"tweener-{id(self)}" self.tweens = [] self.callbacks = [] self.last_frame = get_time()
def repeat(cls, seconds, func, until=None): n = get_time() t = Timer(n + seconds, func, repeating=seconds, until=n + until) cls.timers.add(t) return t