class HelpDisplay(InstructionGroup): ''' Help Overlay. |---------------------| |mode | | (O O O O) | | O O O O | | O O O O | |return help| |---------------------| ''' def __init__(self, mode): super(HelpDisplay, self).__init__() self.mode = mode # Start levels self.start_box_color = Color(0, 1, 0, 0.2) self.start_box = Rectangle(pos=(0.1 * w, 0.7 * h), size=(0.8 * w, 0.2 * h)) self.start_text_color = Color(0, 1, 0, 0.8) if mode == 'lmode': start_text = "Start off with these levels" cpos = (0.75 * w, 0.88 * h) elif mode == 'gmode': start_text = "Complete the learning mode to unlock the first level" cpos = (0.6 * w, 0.88 * h) self.start_text = CLabelRect(text=start_text, font_size=font_sz / 4, font_name=font_name, cpos=cpos) self.add(self.start_box_color) self.add(self.start_box) self.add(self.start_text_color) self.add(self.start_text) # Locked levels self.locked_box_color = Color(1, 1, 0, 0.2) self.locked_box = Rectangle(pos=(0.1 * w, 0.2 * h), size=(0.8 * w, 0.5 * h)) self.locked_text_color = Color(1, 1, 0, 0.8) if mode == 'lmode': locked_text = "Complete the easier levels to unlock new ones" elif mode == 'gmode': locked_text = "Pass the easier level to unlock the next one" self.locked_text = CLabelRect(text=locked_text, font_size=font_sz / 4, font_name=font_name, cpos=(0.65 * w, 0.25 * h)) self.add(self.locked_box_color) self.add(self.locked_box) self.add(self.locked_text_color) self.add(self.locked_text) def on_layout(self, win_size): w, h = win_size self.start_box.pos = (0.1 * w, 0.7 * h) self.start_box.size = (0.8 * w, 0.2 * h) if self.mode == 'lmode': self.start_text.set_cpos((0.75 * w, 0.88 * h)) elif self.mode == 'gmode': self.start_text.set_cpos((0.6 * w, 0.88 * h)) self.locked_box.pos = (0.1 * w, 0.2 * h) self.locked_box.size = (0.8 * w, 0.5 * h) self.locked_text.set_cpos((0.65 * w, 0.25 * h))
class TimerDisplay(InstructionGroup): def __init__(self): super(TimerDisplay, self).__init__() self.duration = 30. self.start_x = Window.width * 0.2 self.end_x = Window.width * .85 self.start_y = Window.height * .2 self.end_y = Window.height * .25 # Initialize moving timer self.color = Color(0, 1, 0) # start off green self.bar = Rectangle(pos=(self.start_x, self.start_y), size=(self.end_x - self.start_x, self.end_y - self.start_y)) self.add(self.color) self.add(self.bar) self.remaining_time = CLabelRect( cpos=(self.end_x + 50, self.start_y), text=str(int(self.duration)), font_size=(self.end_y - self.start_y) / 2, font_name="assets/AtlantisInternational") self.add(self.remaining_time) # Vertical borders self.border_left = Line(width=2) self.border_mid = Line(width=2) self.border_right = Line(width=2) self.add(self.border_left) self.add(self.border_right) self.add(self.border_mid) # Horizontal borders self.border_bot = Line(width=2) self.border_top = Line(width=2) self.add(self.border_bot) self.add(self.border_top) self.on_layout(Window.size) def reset(self, duration=30.): self.duration = duration self.color.rgb = (0, 1, 0) self.bar.size = (self.end_x - self.start_x, self.end_y - self.start_y) self.remaining_time.set_text(str(int(self.duration))) def get_width(self, time): t = (self.end_x - self.start_x) * (1 - time / self.duration) return t def on_update(self, time_elapsed): if time_elapsed > self.duration: return 'end of game' if time_elapsed >= 0: self.bar.size = (self.get_width(time_elapsed), self.end_y - self.start_y) # update remaining time self.remaining_time.set_text(str(int(self.duration - time_elapsed))) # update bar timer color self.color.rgb = (min(time_elapsed / self.duration * 2, 1), min(1, (1 - time_elapsed / self.duration) * 2), 0) def on_layout(self, win_size): self.start_x = win_size[0] * 0.2 self.end_x = win_size[0] * .85 self.start_y = win_size[1] * .2 self.end_y = win_size[1] * .25 self.bar.pos = (self.start_x, self.start_y) self.border_left.points = [(self.start_x, self.start_y), (self.start_x, self.end_y)] self.border_mid.points = [ ((self.start_x + self.end_x) / 2, self.start_y), ((self.start_x + self.end_x) / 2, self.end_y) ] self.border_right.points = [(self.end_x, self.start_y), (self.end_x, self.end_y)] self.border_bot.points = [(self.start_x, self.start_y), (self.end_x, self.start_y)] self.border_top.points = [( self.start_x, self.end_y, ), (self.end_x, self.end_y)] self.remaining_time.set_cpos((self.end_x + 50, self.start_y))
class PhysicsBubble(InstructionGroup): """ This module is a drag-and-release physics-based bubble that plays a sound upon colliding with another collidable object, including the sandbox edges. """ name = 'PhysicsBubble' def __init__(self, norm, sandbox, pos, vel, pitch, timbre, color, bounces, handler, gravity=False, callback=None): """ :param norm: normalizer :param sandbox: client's sandbox :param pos: initial position :param vel: initial velocity :param pitch: MIDI pitch value, where 60 is middle C :param timbre: type of waveform, e.g. 'sine' or 'sawtooth' :param color: 3-tuple of RGB color :param bounces: number of times the bubble bounces before fading away :param gravity: whether or not the bubble is subjected to downwards gravity :param callback: the sound function that is called when the bubble bounces """ super(PhysicsBubble, self).__init__() self.norm = norm self.sandbox = sandbox self.r = self.norm.nv(40) self.pos = np.array(pos, dtype=np.float) self.vel = 2 * np.array(vel, dtype=np.float) self.pitch = pitch self.timbre = timbre self.color = Color(*color) self.text_color = Color(0, 0, 0) self.bounces = bounces self.gravity = gravity self.callback = callback self.handler = handler self.text = CLabelRect(cpos=pos, text=str(self.bounces)) self.bubble = self.timbre_to_shape(self.timbre, pos) self.add(self.color) self.add(self.bubble) self.add(self.text_color) self.add(self.text) # have the bubble fade away when self.bounces = 0 self.fade_anim = KFAnim((0, 1), (0.25, 0)) self.time = 0 self.on_update(0) def timbre_to_shape(self, timbre, pos): if timbre == 'sine': return CEllipse(cpos=pos, size=self.norm.nt((80, 80)), segments=20) elif timbre == 'triangle': return CEllipse(cpos=pos, size=self.norm.nt((90, 90)), segments=3) elif timbre == 'square': return CRectangle(cpos=pos, size=self.norm.nt((80, 80))) elif timbre == 'sawtooth': # square rotated 45 degrees return CEllipse(cpos=pos, size=self.norm.nt((90, 90)), segments=4) def on_update(self, dt): if self.gravity: self.vel += downwards_gravity * dt self.pos += self.vel * dt else: self.pos += self.vel * dt if self.bounces > 0: if self.check_for_block_collisions() and self.callback is not None: self.callback(self.pitch, self.timbre) if self.check_for_collisions() and self.callback is not None: self.callback(self.pitch, self.timbre) # second condition checks if bubble hasn't been moving but there's no gravity -- # since bubble would be on the screen forever without making sound, fade it away if self.bounces <= 0 or (not self.gravity and np.linalg.norm(self.vel) == 0): self.color.a = self.fade_anim.eval(self.time) self.time += dt self.bubble.set_cpos(self.pos) self.text.set_cpos(self.pos) return self.fade_anim.is_active(self.time) def check_for_collisions(self): bottom = self.sandbox.pos[1] top = self.sandbox.pos[1] + self.sandbox.height left = self.sandbox.pos[0] right = self.sandbox.pos[0] + self.sandbox.width # collision with bottom if self.pos[1] - self.r < bottom: self.vel[1] = -self.vel[ 1] * damping_factor if self.gravity else -self.vel[1] self.pos[1] = bottom + self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) return True # collision with top if self.pos[1] + self.r > top: self.vel[1] = -self.vel[1] self.pos[1] = top - self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) return True # collision with left if self.pos[0] - self.r < left: self.vel[0] = -self.vel[0] self.pos[0] = left + self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) return True # collision with right if self.pos[0] + self.r > right: self.vel[0] = -self.vel[0] self.pos[0] = right - self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) return True def check_for_block_collisions(self): """Check to see if this bubble collided with any SoundBlocks.""" collide = False blocks = self.handler.block_handler.blocks.objects for block in blocks: left_x = block.pos[0] right_x = block.pos[0] + block.size[0] bottom_y = block.pos[1] top_y = block.pos[1] + block.size[1] # going left if self.pos[0] + self.r >= left_x and \ self.pos[0]+self.r <= right_x and \ self.pos[1] >= bottom_y and self.pos[1] <= top_y: self.vel[0] *= -1 self.pos[0] = left_x - self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) block.flash() collide = True # going right if self.pos[0] - self.r <= right_x and \ self.pos[0]-self.r >= left_x and \ self.pos[1] >= bottom_y and self.pos[1] <= top_y: self.vel[0] *= -1 self.pos[0] = right_x + self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) block.flash() collide = True # going up if self.pos[1] + self.r >= bottom_y and \ self.pos[1] + self.r <= top_y and \ self.pos[0] >= left_x and self.pos[0] <= right_x: self.vel[1] *= -1 self.pos[1] = bottom_y - self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) block.flash() collide = True # going down if self.pos[1] - self.r <= top_y and \ self.pos[1]-self.r >= bottom_y and \ self.pos[0] >= left_x and self.pos[0] <= right_x: self.vel[1] *= -1 self.pos[1] = top_y + self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) block.flash() collide = True return collide