예제 #1
0
class MyGame(arcade.Window):
    """
    Main application class.
    """
    def __init__(self):

        # Call the parent class and set up the window
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)

        arcade.set_background_color(arcade.csscolor.CORNFLOWER_BLUE)

    def setup(self, restart=False):
        """ Set up the game here. Call this function to restart the game. """
        self.note = Note(*NOTE_POS)
        self.button = Button(*BUTTON_POS)
        self.clock = Clock(*CLOCK_POS)
        self.strikes = Strikes(*STRIKES_POS)
        self.init_items()
        self.init_person()
        self.init_idcard()
        self.state = State.START_FADE_IN
        self.state_time = 0
        self.day = 1
        self.score = 0
        self.guide_visible = False

        if not restart:
            self.sound_player = None
            arcade.play_sound(assets.sounds['elevator_music'],
                              volume=0.03,
                              looping=True)
            self.set_fullscreen(True)
            self.set_icon(pyglet.image.load("res/image/icon.png"))

    def on_draw(self):
        """ Render the screen. """

        arcade.start_render()

        if self.state in [
                State.START_SCREEN, State.START_FADE_OUT, State.START_FADE_IN
        ]:
            arcade.draw_scaled_texture_rectangle(SCREEN_WIDTH / 2,
                                                 SCREEN_HEIGHT / 2,
                                                 assets.intro_image, 1, 0)
            if self.guide_visible:
                arcade.draw_scaled_texture_rectangle(SCREEN_WIDTH / 2,
                                                     SCREEN_HEIGHT / 2,
                                                     assets.guide_image, 1, 0)
                arcade.draw_scaled_texture_rectangle(SCREEN_WIDTH - 200, 100,
                                                     assets.close_image, .5, 0)
            if self.state == State.START_FADE_OUT:
                arcade.draw_rectangle_filled(
                    SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_WIDTH,
                    SCREEN_HEIGHT,
                    (0, 0, 0, max(int(self.state_time * 255), 0)))
            elif self.state == State.START_FADE_IN:
                arcade.draw_rectangle_filled(
                    SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_WIDTH,
                    SCREEN_HEIGHT,
                    (0, 0, 0, max(int((1 - self.state_time) * 255), 0)))
            return

        arcade.draw_scaled_texture_rectangle(SCREEN_WIDTH / 2,
                                             SCREEN_HEIGHT / 2,
                                             assets.bg_image, 1, 0)
        self.button.draw()

        self.person.draw()
        self.id_card.draw()

        self.clock.draw()
        self.note.draw()
        self.strikes.draw()
        for item in reversed(self.items):
            item.draw()

        if self.state == State.DAY_FADE_IN:
            arcade.draw_rectangle_filled(
                SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_WIDTH,
                SCREEN_HEIGHT,
                (0, 0, 0, max(int((1 - self.state_time) * 255), 0)))
        elif self.state in [State.DAY_FADE_OUT, State.END_FADE_OUT]:
            arcade.draw_rectangle_filled(
                SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_WIDTH,
                SCREEN_HEIGHT, (0, 0, 0, max(int(self.state_time * 255), 0)))
        elif self.state == State.DAY_INTRO:
            arcade.draw_rectangle_filled(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2,
                                         SCREEN_WIDTH, SCREEN_HEIGHT,
                                         arcade.color.BLACK)
            if self.state_time < 4:
                color = (min(int(self.state_time * 255), 255), ) * 3
            else:
                color = (min(int((5 - self.state_time) * 255), 255), ) * 3
            arcade.draw_text(f"Day {['one', 'two', 'three'][self.day-1]}",
                             SCREEN_WIDTH / 2,
                             SCREEN_HEIGHT / 2,
                             color,
                             48,
                             align="center",
                             anchor_x="center",
                             anchor_y="center")
        elif self.state == State.END_SCREEN:
            arcade.draw_rectangle_filled(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2,
                                         SCREEN_WIDTH, SCREEN_HEIGHT,
                                         arcade.color.BLACK)
            text = f"You correctly returned {self.score} lost items."
            if self.strikes.strikes == 3:
                text = f"You have been fired for returning items wrongly.\n\n{text}"
            text = f"{text}\n\n\n\nPress R to play again."
            color = (min(int(self.state_time * 255), 255), ) * 3
            arcade.draw_text(text,
                             SCREEN_WIDTH / 2,
                             SCREEN_HEIGHT / 2,
                             color,
                             48,
                             align="center",
                             anchor_x="center",
                             anchor_y="center")

    def on_update(self, delta):
        """ Game logic """
        self.state_time += delta

        if self.state in [State.END_SCREEN, State.START_SCREEN]:
            return
        elif self.state == State.DAY_FADE_IN:
            if self.state_time > 1:
                self.transition(State.CHOOSING)
            return
        elif self.state == State.START_FADE_IN:
            if self.state_time > 1:
                self.transition(State.START_SCREEN)
            return
        elif self.state == State.START_FADE_OUT:
            if self.state_time > 1:
                self.transition(State.DAY_INTRO)
        elif self.state == State.END_FADE_OUT:
            if self.state_time > 1:
                self.transition(State.END_SCREEN)
        elif self.state == State.DAY_FADE_OUT:
            if self.state_time > 1:
                self.transition(State.DAY_INTRO)
                self.advance_day()
            return
        elif self.state == State.DAY_INTRO:
            if self.state_time > 5:
                self.transition(State.DAY_FADE_IN)
            return

        self.person.update(delta)
        self.clock.update(delta)
        for item in self.items:
            item.update(delta)
        if self.held_item is not None:
            self.held_item.move(self.mouse_x, self.mouse_y)

        if self.state == State.CHOICE_MADE and not self.person.is_traveling:
            self.transition(State.CHOOSING)

        if self.state == State.CHOOSING and self.strikes.strikes == 3:
            self.transition(State.END_FADE_OUT)

        if self.person.state == State.ENTER and not self.person.is_traveling:
            self.person.set_state(State.INTRO)

        elif self.person.state == State.INTRO:
            if not is_playing(self.sound_player):
                self.sound_player = self.person.play_sound("intro")
                self.person.set_state(State.DESCRIBE)

        elif self.person.state == State.DESCRIBE:
            if not is_playing(self.sound_player):
                self.sound_player = self.person.wanted_item.play_sound()
                self.person.set_state(State.WAITING)

        elif self.person.state in [State.AWAY_RIGHT, State.AWAY_WRONG]:
            if not is_playing(self.sound_player):
                if self.person.state == State.AWAY_RIGHT:
                    sound = "right"
                else:
                    sound = "wrong"
                self.sound_player = self.person.play_sound(sound)
                self.person.set_state(State.EXIT)

        elif self.person.state == State.EXIT and not self.person.is_traveling:

            if self.clock.is_completed():
                self.transition(State.DAY_FADE_OUT)
                self.person.state = State.WAITING
                return

            self.init_person()
            self.init_idcard()

    def init_items(self):
        """ Create a 3x4 grid of items """
        self.held_item = None
        self.items = []
        available_items = random.sample(list(assets.items), 12)

        for r in range(3):
            for c in range(4):
                ox, oy = GRID_POS
                rangle = math.pi * 2 * random.random()
                rlength = GRID_ITEM_RAND_MAX_OFFSET * random.random()**.3
                rx, ry = math.cos(rangle) * rlength, math.sin(rangle) * rlength
                self.items.append(
                    Item(ox + rx + GRID_ITEM_SIZE * c,
                         oy + ry + GRID_ITEM_SIZE * r, available_items.pop()))

    def init_person(self):
        """ Create a person that travels to its position """
        self.person = Person(0, SCREEN_HEIGHT + IMAGE_SIZE)
        self.person.travel_to(500, 800, spin=False)
        self.person.set_state(State.ENTER)
        self.person.pick_item(self.items)
        self.note.set_item(self.person.wanted_item.name)
        self.note.set_person_name(self.person.name)
        self.note.randomize_fake()

    def init_idcard(self):
        """
        Inits the id card of the current person with a 30% chance of the
        card being false.
        """
        self.id_card = IdCard(self.person,
                              True if random.randint(0, 100) < 30 else False)

    def transition(self, state):
        """ Changes state """
        self.state = state
        self.state_time = 0

    def advance_day(self):
        """ Move to the next day """
        if self.day == 3:
            self.transition(State.END_SCREEN)
        self.day = (self.day % 3) + 1
        self.note.day = self.day
        self.clock.time = 0
        self.init_items()
        self.init_person()
        self.init_idcard()

    def get_strike(self):
        arcade.play_sound(assets.sounds['strike'], volume=0.1)
        self.strikes.strikes += 1
        if self.strikes.strikes == 3:
            self.transition(State.END_FADE_OUT)

    def on_mouse_press(self, x, y, button, _modifiers):
        """ Handle mouse press """
        if button != 1:  # Left
            return

        if self.held_item is not None:
            return

        for item in self.items:
            if dist(item.x, item.y, x, y) < IMAGE_SIZE / 2 * Item.SCALE * .9:
                self.held_item = item
                break

        if dist(self.button.x, self.button.y, x, y) < 70 * Button.SCALE:
            if self.state == State.CHOOSING:
                new_x = self.person.x - 1.5 * IMAGE_SIZE
                self.person.set_state(State.AWAY_WRONG)
                self.person.travel_to(new_x, SCREEN_HEIGHT + IMAGE_SIZE)
                self.transition(State.CHOICE_MADE)
                if not self.note.is_fake and not self.id_card.is_false():
                    self.get_strike()

        if self.state == State.START_SCREEN:
            if self.guide_visible:
                self.guide_visible = False
                arcade.play_sound(assets.sounds['click'])
            elif 350 < y and y < 600:
                if 1030 < x and x < 1500:
                    self.guide_visible = True
                    arcade.play_sound(assets.sounds['click'])
                if 470 < x and x < 940:
                    self.transition(State.START_FADE_OUT)
                    arcade.play_sound(assets.sounds['click'])

    def on_mouse_release(self, x, y, button, _modifiers):
        """ Handle mouse release """
        if button != 1:  # Left
            return

        if self.held_item is not None:
            if self.state == State.CHOOSING \
               and dist(x, y, self.person.x, self.person.y) < ITEM_PLACE_RANGE:
                self.transition(State.CHOICE_MADE)
                if self.person.wanted_item.name == self.held_item.name:
                    new_x = self.person.x + 1.5 * IMAGE_SIZE
                    self.person.set_state(State.AWAY_RIGHT)
                    if self.note.is_fake or self.id_card.is_false():
                        self.get_strike()
                    else:
                        self.score += 1
                else:
                    new_x = self.person.x - 1.5 * IMAGE_SIZE
                    self.person.set_state(State.AWAY_WRONG)
                    self.get_strike()

                self.person.travel_to(new_x, SCREEN_HEIGHT + IMAGE_SIZE)
                self.items.remove(self.held_item)
                self.person.give_item(self.held_item)

            self.held_item.x = self.held_item.orig_x
            self.held_item.y = self.held_item.orig_y
            self.held_item = None

    def on_mouse_motion(self, x, y, _dx, _dy):
        """ Handle mouse release """
        self.mouse_x = x
        self.mouse_y = y

    def on_key_press(self, key, modifiers):
        """Called whenever a key is pressed. """
        if key == arcade.key.F:
            self.set_fullscreen(not self.fullscreen)
            width, height = self.get_size()
            self.set_viewport(0, width, 0, height)
        elif key in [arcade.key.ESCAPE, arcade.key.Q]:
            exit()
        elif key == arcade.key.R:
            if self.state == State.END_SCREEN:
                self.setup(restart=True)
예제 #2
0
파일: lane.py 프로젝트: daveisadork/Racer
class Lane:
    def __init__(self, tree_type="sportsman", delay=0.5, start=None, lane="left",
                 computer=True, perfect=0.0, rollout=0.220, cmin=-0.009,
                 cmax=0.115, surface=None, background=None):
        self.perfect = perfect
        self.rollout = rollout
        self.cmin = cmin
        self.cmax = cmax
        self.surface = surface
        self.rect = self.surface.get_rect()
        self.dirty_rects = []
        self.dirty = False
        self.tree_type = tree_type
        if tree_type == "sportsman":
            self.multiplier = 3
        elif tree_type == "pro":
            self.multiplier = 1
        self.state = None
        self.launched_time = None
        self.start_time = None
        self.foul = threading.Event()
        self.launched = threading.Event()
        self.pre_staged = threading.Event()
        self.staged = threading.Event()
        self.flashing = threading.Event()
        self.y1 = threading.Event()
        self.y2 = threading.Event()
        self.y3 = threading.Event()
        self.g = threading.Event()
        self.reaction = None
        self.dial_in = 0.0
        self.log = []
        self.computer = computer
        self.start = start
        self.delay = delay
        self.state = 0
        self.total_delay = self.delay * self.multiplier
        self.start_time = None
        self.clock_log = []
        clock_rect = pygame.Rect(
            0, self.rect.height - self.rect.height / 7.0,
            self.rect.width, self.rect.height / 7.0)
        clock_rect.bottom = self.rect.height
        self.clock = Clock(clock_rect)
        self.background = pygame.Surface(
            (self.rect.width, self.rect.height),
            flags=pygame.SRCALPHA)
        self.background.blit(
            background,
            (0, 0),
            area=(
                self.surface.get_offset(),
                (self.rect.width, self.rect.height)))
        self.surface.blit(self.background, (0, 0))
        self.lane = lane.lower()
        light_width = (self.rect.height / 7) - (self.rect.height / 35.0)
        self.lights = [
            Light(light_width, light_type='staging'),
            Light(light_width, light_type='staging'),
            Light(light_width, light_type='yellow'),
            Light(light_width, light_type='yellow'),
            Light(light_width, light_type='yellow'),
            Light(light_width, light_type='green'),
            Light(light_width, light_type='red')
        ]
        #if self.lane == "left":
        #    self.offset = int(round(self.rect.width * 0.49, 0))
        #else:
        self.offset = int(round(self.rect.height / 16.5, 0))
        total_offset = self.rect.height / 20.575
        counter = 0
        for light in self.lights:
            if self.lane == "right":
                light.rect.left = self.offset
            else:
                light.rect.right = self.rect.right - self.offset
            light.rect.top = int(round(total_offset, 0))
            if counter == 0:
                total_offset += light_width - (light_width / 11.0)
            elif counter == 1:
                total_offset +=  light_width / 2.25
            else:
                total_offset += (light_width / 10.0) + light_width
                #total_offset += (self.rect.height / 700.0) * 1.49

            counter += 1

    def reset(self):
        self.start_time = None
        self.launched_time = None
        self.foul.clear()
        self.flashing.clear()
        self.launched.clear()
        self.pre_staged.clear()
        self.staged.clear()
        self.y1.clear()
        self.y2.clear()
        self.y3.clear()
        self.g.clear()
        self.clock.reset()
        self.clock_log = []
        for light in self.lights:
            light.off()
        self.pre_stage()

    def pre_stage(self):
        self.pre_staged.set()
        self.state = 0
        self.lights[0].on()
        if self.computer:
            self.stage()

    def stage(self):
        self.state = 1
        self.lights[1].on()
        self.staged.set()

    def _start(self):
        threading.Thread(None, self.timer, name=self.lane + " timer()").start()

    def launch(self):
        self.start.wait()
        if self.computer:
            if self.cmin == self.cmax:
                computer_delay = self.cmin
            else:
                computer_delay = random.randrange(
                    (self.total_delay * 1000) + (self.cmin * 1000),
                    (self.total_delay * 1000 ) + (self.cmax * 1000), 1) / 1000.0
            self.launched_time = self.start_time + computer_delay
            self.launched.set()
        else:
            self.launched.wait()
            self.launched_time += self.rollout
        self.reaction = self.launched_time + self.perfect
        self.reaction -= (self.total_delay + self.start_time)
        if not self.total_delay + self.start_time - time.time() < 0:
            time.sleep(self.start_time + self.total_delay + self.reaction -self.perfect - time.time())
        if self.reaction < self.perfect:
            self.red()
        self.pre_staged.clear()
        self.lights[0].off()
        self.staged.clear()
        self.lights[1].off()
        self.clock.draw(self.reaction)
        self.log.append(self.reaction)

    def timer(self):
        launch = threading.Thread(None,
            self.launch, name=self.lane + " launch()").start()
        threading.Thread(None,
            self.yellow1, name=self.lane + " yellow1()").start()
        threading.Thread(None,
            self.yellow2, name=self.lane + " yellow2()").start()
        threading.Thread(None,
            self.yellow3, name=self.lane + " yellow3()").start()
        threading.Thread(None,
            self.green, name=self.lane + " green()").start()
        self.state = 2

    def light(self, number=0):
        if self.foul.is_set():
            return False
        if self.tree_type == "pro":
            if number == 3:
                number = 1
            else:
                number = 0
        while time.time() < self.start_time + (self.delay * number):
            if self.foul.is_set():
                return False
        return True

    def yellow1(self):
        self.y1.wait()
        if not self.foul.is_set():
            self.lights[2].on()
            time.sleep(self.delay)
        if not self.foul.is_set():
            self.lights[2].off()

    def yellow2(self):
        if self.tree_type == "pro":
            self.y1.wait()
        else:
            self.y2.wait()
        if not self.foul.is_set():
            self.lights[3].on()
            time.sleep(self.delay)
        if not self.foul.is_set():
            self.lights[3].off()

    def yellow3(self):
        if self.tree_type == "pro":
            self.y1.wait()
        else:
            self.y3.wait()
        if not self.foul.is_set():
            self.lights[4].on()
            time.sleep(self.delay)
        if not self.foul.is_set():
            self.lights[4].off()

    def green(self):
        self.g.wait()
        if not self.foul.is_set():
            self.lights[5].on()

    def red(self):
        self.foul.set()
        self.lights[6].on()
        self.lights[5].off()

    def win(self):
        self.flashing.set()
        threading.Thread(None, self.flash, name=self.lane + " flash()").start()

    def flash(self):
        try:
            while self.flashing.is_set():
                for light in self.lights[2:5]:
                    light.on()
                time.sleep(0.5)
                for light in self.lights[2:5]:
                    light.off()
                time.sleep(0.5)
            for light in self.lights[2:5]:
                light.off()
        except:
            pass

    def draw(self):
        dirty_objects = []
        for light in self.lights + [self.clock]:
            if light.dirty:
                dirty_objects.append(light)
                self.dirty_rects.append(
                    light.rect.move(self.surface.get_offset()))
                light.dirty = False
                # if self.dirty:
                #     self.dirty = self.dirty.union(light.rect)
                # else:
                #     self.dirty = light.rect
        # if self.dirty:
        #     self.surface.blit(
        #         self.background,
        #         (self.dirty.left, self.dirty.top),
        #         area=self.dirty)
        self.dirty = bool(len(dirty_objects))
        for light in dirty_objects:
            self.surface.blit(self.background,
                              (light.rect.left, light.rect.top),
                              area=light.rect)
            self.surface.blit(light.surface, (light.rect.left, light.rect.top))
예제 #3
0
class Sequency(threading.Thread):
    def __init__(self, midi_in, midi_out):
        super(Sequency, self).__init__()
        self._midi_out = midi_out
        self._midi_in = midi_in
        # Clock messages are ignored by default
        midi_in.ignore_types(timing=False)
        midi_in.set_callback(self._handle_input)

        self._lp = LaunchpadMini(midi_in, midi_out)
        self._controller = MidiToControl()

        self._boot_combo = []
        self._reboot_set = False
        self._control_mode = CONTROL_MODE.DEFAULT

        sequence_states = self._load_from_state()

        self._clock = Clock()
        self._sequences = []
        self._active_sequence = 0
        for i in range(0, 8):
            self._sequences.append(
                Sequencer(i, self._on_trigger, sequence_states[i]))

        self.start()

    def run(self):
        self.done = False

        time.sleep(5)
        self._startup_animation()

        self._draw_active_sequence()
        self._draw()

        while not self.done:
            time.sleep(0.02)

    def kill(self):
        self._write_save_state_raw()
        self._lp.turn_all_pads_off()

    def _startup_animation(self):
        for colour in [COLOUR_RED, COLOUR_ORANGE, COLOUR_GREEN]:
            for brightness in [
                    BRIGHTNESS_LOW, BRIGHTNESS_MEDIUM, BRIGHTNESS_HIGH
            ]:
                for coords in CIRCLE_COORDS:
                    self._lp.turn_pad_on(coords, colour, brightness)
                    time.sleep(0.01)
        self._lp.turn_all_pads_off()

    def _get_sequence_colour(self, index):
        if self._sequences[index].mute:
            return COLOUR_RED
        elif index == self._active_sequence:
            return COLOUR_GREEN
        return COLOUR_ORANGE

    def _on_trigger(self, index, channel, note):
        self._midi_out.send_message([143 + channel, note, 64])
        bsp_lp = threading.Timer(0.06, self._end_trigger_bsp,
                                 [index, channel, note])
        bsp_lp.start()

        colour = self._get_sequence_colour(index)
        self._lp.turn_pad_on(('T', index), colour, BRIGHTNESS_HIGH)
        t_lp = threading.Timer(0.1, self._end_trigger_lp, [index])
        t_lp.start()

    def _end_trigger_lp(self, index):
        colour = self._get_sequence_colour(index)
        self._lp.turn_pad_on(('T', index), colour, BRIGHTNESS_LOW)

    def _end_trigger_bsp(self, index, channel, note):
        self._midi_out.send_message([127 + channel, note, 64])

    def _handle_input(self, event, data=None):
        message, deltatime = event
        if message[0] is TIMING_CLOCK:
            self._clock.clock_pulse()
            for seq in self._sequences:
                seq.clock_pulse()
        elif message[0] is SONG_START:
            self._clock.reset()
            for seq in self._sequences:
                seq.reset()
        elif message[0] is SONG_STOP or message[0] is SONG_CONTINUE:
            pass
        else:
            command = self._controller.map_midi_to_command(
                message, self._control_mode)
            self._handle_command(command)

        self._draw()

    def _handle_command(self, command):
        if not command:
            return

        command_type, args = command
        if command_type is COMMAND.NONE:
            return
        elif command_type is COMMAND.CHANGE_CONTROL_MODE:
            self._change_control_mode(args[0])
        elif command_type is COMMAND.CHANGE_SEQUENCE:
            self._change_active_sequence(args[0])
        elif command_type is COMMAND.TOGGLE_MUTE:
            self._toggle_mute()
        elif command_type is COMMAND.SET_MUTE:
            self._set_mute(args[0], args[1])
        elif command_type is COMMAND.CHANGE_MODE:
            self._change_sequence_mode(args[0])
        elif command_type is COMMAND.MANUAL_INPUT:
            self._input_manual_step(args)
        elif command_type is COMMAND.CHANGE_LENGTH_INC:
            self._increment_sequence_length(args[0])
        elif command_type is COMMAND.CHANGE_LENGTH_DEC:
            self._decrement_sequence_length(args[0])
        elif command_type is COMMAND.CHANGE_FILL_INC:
            self._increment_fill(args[0])
        elif command_type is COMMAND.CHANGE_FILL_DEC:
            self._decrement_fill(args[0])
        elif command_type is COMMAND.CHANGE_OFFSET_INC:
            self._increment_sequence_offset(args[0])
        elif command_type is COMMAND.CHANGE_OFFSET_DEC:
            self._decrement_sequence_offset(args[0])
        elif command_type is COMMAND.CHANGE_CHANNEL_INC:
            self._increment_sequence_channel(args[0])
        elif command_type is COMMAND.CHANGE_CHANNEL_DEC:
            self._decrement_sequence_channel(args[0])
        elif command_type is COMMAND.CHANGE_NOTE_REL:
            self._change_sequence_note(args[0], args[1])
        elif command_type is COMMAND.BOOT_COMBO:
            self._handle_boot_combo(args)
        else:
            print('Unkown command', command_type, args)

        self._write_save_state()

    def _change_active_sequence(self, index):
        self._active_sequence = index
        self._draw_active_sequence()

    def _change_control_mode(self, mode):
        self._control_mode = mode
        self._lp.turn_all_pads_off()
        self._draw_active_sequence()
        self._draw()

    def _toggle_mute(self):
        self._sequences[self._active_sequence].toggle_mute()
        self._draw_active_sequence()

    def _set_mute(self, index, mute):
        self._sequences[index].set_mute(mute)
        self._draw_active_sequence()

    def _change_sequence_mode(self, mode):
        active_sequence = self._sequences[self._active_sequence]
        active_sequence.change_mode(mode)
        self._draw_active_sequence()

    def _increment_sequence_length(self, index):
        self._active_sequence = index
        active_sequence = self._sequences[self._active_sequence]
        active_sequence.increment_length()
        self._draw_active_sequence()

    def _decrement_sequence_length(self, index):
        self._active_sequence = index
        active_sequence = self._sequences[self._active_sequence]
        active_sequence.decrement_length()
        self._draw_active_sequence()

    def _increment_fill(self, index):
        self._active_sequence = index
        active_sequence = self._sequences[self._active_sequence]
        active_sequence.increment_fill()
        self._draw_active_sequence()

    def _decrement_fill(self, index):
        self._active_sequence = index
        active_sequence = self._sequences[self._active_sequence]
        active_sequence.decrement_fill()
        self._draw_active_sequence()

    def _increment_sequence_offset(self, index):
        self._active_sequence = index
        active_sequence = self._sequences[self._active_sequence]
        active_sequence.increment_offset()
        self._draw_active_sequence()

    def _decrement_sequence_offset(self, index):
        self._active_sequence = index
        active_sequence = self._sequences[self._active_sequence]
        active_sequence.decrement_offset()
        self._draw_active_sequence()

    def _increment_sequence_channel(self, index):
        self._active_sequence = index
        active_sequence = self._sequences[self._active_sequence]
        active_sequence.increment_channel()
        self._draw_active_sequence()

    def _decrement_sequence_channel(self, index):
        self._active_sequence = index
        active_sequence = self._sequences[self._active_sequence]
        active_sequence.decrement_channel()
        self._draw_active_sequence()

    def _change_sequence_note(self, index, delta):
        self._active_sequence = index
        active_sequence = self._sequences[self._active_sequence]
        active_sequence.change_note(delta)
        self._draw_active_sequence()

    def _handle_boot_combo(self, key_event):
        if self._reboot_set:
            return

        key, on = key_event
        if on:
            self._boot_combo.append(key)
            if len(self._boot_combo) == 4:
                self._reboot_set = True
                for coords in self._boot_combo:
                    self._lp.turn_pad_on(coords, COLOUR_GREEN, BRIGHTNESS_HIGH)
                os.system('sudo sh /home/pi/measure_pi/swap_config.sh')
        else:
            self._boot_combo.remove(key)
            self._lp.turn_pad_off(key)

    def _input_manual_step(self, coords):
        if coords in CIRCLE_COORDS:
            step = CIRCLE_COORDS.index(coords)
            active_sequence = self._sequences[self._active_sequence]
            active_sequence.manual_input(step)

    def _draw_active_sequence(self):
        for i, sequence in enumerate(self._sequences):
            colour = COLOUR_ORANGE
            if sequence.mute:
                colour = COLOUR_RED
            if i == self._active_sequence:
                colour = COLOUR_GREEN
            self._lp.turn_pad_on(('T', i), colour)

        active_sequence = self._sequences[self._active_sequence]

        if active_sequence.mute:
            self._lp.turn_pad_on((8, 0), COLOUR_RED, BRIGHTNESS_HIGH)
        else:
            self._lp.turn_pad_off((8, 0))

        if active_sequence._mode is EUCLIDIAN:
            self._lp.turn_pad_on((8, 1), COLOUR_GREEN, BRIGHTNESS_HIGH)
        else:
            self._lp.turn_pad_off((8, 1))
        if active_sequence._mode is REV_EUCLIDIAN:
            self._lp.turn_pad_on((8, 2), COLOUR_GREEN, BRIGHTNESS_HIGH)
        else:
            self._lp.turn_pad_off((8, 2))
        if active_sequence._mode is MANUAL:
            self._lp.turn_pad_on((8, 3), COLOUR_GREEN, BRIGHTNESS_HIGH)
        else:
            self._lp.turn_pad_off((8, 3))

    def _draw(self):
        clock_state = self._clock.draw()
        for i, message in enumerate(clock_state):
            colour, brightness = message
            self._lp.turn_pad_on((8, 7 - i), colour, brightness)

        if self._control_mode is CONTROL_MODE.DEFAULT:
            self._draw_sequence()
        elif self._control_mode is CONTROL_MODE.SETTINGS:
            self._draw_settings()

    def _draw_sequence(self):
        active_sequence = self._sequences[self._active_sequence]
        sequence_state = active_sequence.draw()
        for i, message in enumerate(sequence_state):
            colour, brightness = message
            self._lp.turn_pad_on(CIRCLE_COORDS[i], colour, brightness)

    def _draw_settings(self):
        active_sequence = self._sequences[self._active_sequence]
        settings_state = active_sequence.draw_settings()
        for i, message in enumerate(settings_state):
            colour, brightness = message
            self._lp.turn_pad_on(SETTINGS_COORDS[i], colour, brightness)

    def _load_from_state(self):
        try:
            f = open('state.txt', 'r')
            lines = f.readlines()
            sequence_states = []
            parsing_sequences = False
            for line in lines:
                if parsing_sequences:
                    sequence_states.append(line)
                if line == 'seqs\n':
                    parsing_sequences = True

            f.close()

            # If somehow we're missing states fill up to 8
            sequence_states.extend([None] * (8 - len(sequence_states)))
            return sequence_states
        except:
            print('Invalid state file, resetting defaults')
            return [None] * 8

    @debounce(1)
    def _write_save_state(self):
        self._write_save_state_raw()

    def _write_save_state_raw(self):
        state = 'seqs'
        for seq in self._sequences:
            state += '\n'
            state += seq.get_save_state()

        f = open('state.txt', 'w')
        f.write(state)
        f.close()