Exemplo n.º 1
0
 def render_value(label: str, value: object,
                  formatter: VariableFormatter) -> Optional[Any]:
     label_width = 60
     value_size = (
         60 if label == 'dyaw' else 80,
         ig.get_text_line_height() +
         2 * ig.get_style().frame_padding[1],
     )
     ig.push_item_width(label_width)
     ig.selectable(label, width=label_width)
     ig.pop_item_width()
     ig.same_line()
     new_value, _, _ = ui.render_variable_value('value-' + label, value,
                                                formatter, value_size)
     return None if new_value is None else new_value.value
Exemplo n.º 2
0
def render_frame_slider(
    id: str,
    current_frame: int,
    num_frames: int,
    loaded_frames: List[int] = [],
) -> Maybe[int]:
    ig.push_id(id)

    pos = ig.get_cursor_pos()
    pos = (
        pos[0] + ig.get_window_position()[0],
        pos[1] + ig.get_window_position()[1] - ig.get_scroll_y(),
    )
    width = ig.get_content_region_available_width()

    ig.push_item_width(width)
    changed, new_frame = ig.slider_int(
        '##slider',
        current_frame,
        0,
        num_frames - 1,
    )
    ig.pop_item_width()

    dl = ig.get_window_draw_list()
    for frame in loaded_frames:
        line_pos = pos[0] + frame / num_frames * width
        dl.add_line(
            line_pos,
            pos[1] + 13,
            line_pos,
            pos[1] + 18,
            ig.get_color_u32_rgba(1, 0, 0, 1),
        )

    ig.pop_id()

    if changed:
        return Just(new_frame)
    else:
        return None
Exemplo n.º 3
0
def render_input_text_with_error(
    id: str,
    value: str,
    buffer_size: int,
    width: int,
    validate: Callable[[str], T],
) -> Maybe[T]:
    ig.push_id(id)

    cursor_pos = ig.get_cursor_pos()
    cursor_pos = (
        ig.get_window_position()[0] + cursor_pos[0],
        ig.get_window_position()[1] + cursor_pos[1] - ig.get_scroll_y(),
    )

    ig.push_item_width(width)
    changed, new_value = ig.input_text('##input', value, buffer_size)
    ig.pop_item_width()

    result_value = None
    if changed:
        try:
            result_value = Just(validate(new_value))
        except:
            # TODO: Show error message
            dl = ig.get_window_draw_list()
            dl.add_rect(
                cursor_pos[0],
                cursor_pos[1],
                cursor_pos[0] + width,
                cursor_pos[1] + ig.get_text_line_height() +
                2 * ig.get_style().frame_padding[1],
                ig.get_color_u32_rgba(1, 0, 0, 1),
            )
            new_value = value

    ig.pop_id()
    return result_value
Exemplo n.º 4
0
def _render_text(
    value: T,
    formatter: VariableFormatter,
    size: Tuple[int, int],
    highlight: bool,
) -> Tuple[Maybe[T], bool, bool]:
    editing = use_state('editing', False)
    initial_focus = use_state('initial-focus', False)

    if not editing.value:
        clicked, _ = ig.selectable(
            dcast(str, formatter.output(value)) + '##text',
            highlight,
            width=size[0],
            height=size[1],
            flags=ig.SELECTABLE_ALLOW_DOUBLE_CLICK,
        )

        if clicked:
            if ig.is_mouse_double_clicked():
                editing.value = True
                initial_focus.value = False

        pressed = ig.is_item_hovered() and ig.is_mouse_clicked()

        return None, clicked, pressed

    cursor_pos = ig.get_cursor_pos()
    cursor_pos = (
        ig.get_window_position()[0] + cursor_pos[0],
        ig.get_window_position()[1] + cursor_pos[1] - ig.get_scroll_y(),
    )

    ig.push_item_width(size[0])
    value_text = dcast(str, formatter.output(value))
    buffer_size = len(value_text) + ig.get_clipboard_length() + 1000
    _, input = ig.input_text('##text-edit', value_text, buffer_size)
    ig.pop_item_width()

    if not initial_focus.value:
        ig.set_keyboard_focus_here(-1)
        initial_focus.value = True
    elif not ig.is_item_active():
        editing.value = False

    try:
        input_value = formatter.input(input)
        assert type(input_value) is type(value)
        if input_value != value:
            return Just(cast(T, input_value)), False, False
    except:
        # TODO: Show error message
        dl = ig.get_window_draw_list()
        dl.add_rect(
            cursor_pos[0],
            cursor_pos[1],
            cursor_pos[0] + size[0],
            cursor_pos[1] + ig.get_text_line_height() +
            2 * ig.get_style().frame_padding[1],
            ig.get_color_u32_rgba(1, 0, 0, 1),
        )

    return None, False, False
Exemplo n.º 5
0
  def render_left_column(self, framebuffer_size: Tuple[int, int]) -> None:
    total_height = ig.get_window_height() - ig.get_frame_height() # subtract menu bar
    slider_space = 45

    wall_hitbox_radius = use_state('wall-hitbox-radius', 50)
    wall_hitbox_options = [0, 24, 50, 110]

    hovered_surface: Ref[Optional[int]] = use_state('hovered-surface', None)
    new_hovered_surface: Optional[int] = None
    hidden_surfaces_by_area = \
      use_state('hidden-surfaces', cast(Dict[Tuple[int, int], Set[int]], {})).value

    current_area = (
      dcast(int, self.model.get(Variable('level-num').with_frame(self.model.selected_frame))),
      dcast(int, self.model.get(Variable('area-index').with_frame(self.model.selected_frame))),
    )
    hidden_surfaces = hidden_surfaces_by_area.setdefault(current_area, set())

    log.timer.begin('gview1')
    ig.begin_child(
      'Game View 1',
      height=int(total_height // 2) - slider_space // 2,
      border=True,
    )
    hovered_surface_1 = ui.render_game_view_rotate(
      'game-view-1',
      framebuffer_size,
      self.model,
      wall_hitbox_radius.value,
      hovered_surface.value,
      hidden_surfaces,
    )

    ig.set_cursor_pos((10.0, ig.get_window_height() - 30))
    ig.text('wall radius')
    ig.same_line()
    ig.push_item_width(50)
    _, index = ig.combo(
      '##wall-hitbox-radius',
      wall_hitbox_options.index(wall_hitbox_radius.value),
      list(map(str, wall_hitbox_options)),
    )
    wall_hitbox_radius.value = wall_hitbox_options[index]
    ig.pop_item_width()

    ig.end_child()
    log.timer.end()

    log.timer.begin('gview2')
    ig.begin_child(
      'Game View 2',
      height=int(total_height // 2) - slider_space // 2,
      border=True,
    )
    hovered_surface_2 = ui.render_game_view_birds_eye(
      'game-view-2',
      framebuffer_size,
      self.model,
      wall_hitbox_radius.value,
      hovered_surface.value,
      hidden_surfaces,
    )
    ig.end_child()
    log.timer.end()

    new_hovered_surface = hovered_surface_1 or hovered_surface_2
    if new_hovered_surface is not None and ig.is_mouse_clicked(1):
      ig.open_popup('surface-ctx')
      hovered_surface.value = new_hovered_surface

    if ig.begin_popup('surface-ctx'):
      if hovered_surface.value is not None:
        if hovered_surface.value in hidden_surfaces:
          if ig.menu_item('Show')[0]:
            hidden_surfaces.remove(hovered_surface.value)
        else:
          if ig.menu_item('Hide')[0]:
            hidden_surfaces.add(hovered_surface.value)
        if ig.menu_item('Properties')[0]:
          self.variable_explorer.open_surface_tab(hovered_surface.value)
      ig.end_popup()
    else:
      hovered_surface.value = new_hovered_surface

    if hovered_surface.value is not None and ig.is_mouse_clicked(2):
      if hovered_surface.value in hidden_surfaces:
        hidden_surfaces.remove(hovered_surface.value)
      else:
        hidden_surfaces.add(hovered_surface.value)


    speed_options = [0.05, 0.25, 0.5, 1, 2, 4]
    saved_play_direction = use_state('saved-play-direction', 0)
    saved_speed_index = use_state('saved-speed-index', 3)

    play_direction = saved_play_direction.value
    speed_index = saved_speed_index.value

    if play_direction == 0:
      frame_advance = 0
      play_override = 0

      def control(name: str, speed: int) -> None:
        nonlocal frame_advance, play_override
        x = input_down_gradual(name, 0.25)
        if x == 1.0:
          play_override = speed
        elif input_pressed(name):
          frame_advance += speed
      control('frame-next', 1)
      control('frame-next-alt', 1)
      control('frame-prev', -1)
      control('frame-prev-alt', -1)
      control('frame-next-fast', 10)
      control('frame-prev-fast', -10)

      if play_override != 0:
        if abs(play_override) in speed_options:
          speed_index = speed_options.index(abs(play_override))
        else:
          speed_index = len(speed_options) - 1
        play_direction = 1 if play_override > 0 else -1
      else:
        self.model.selected_frame += frame_advance

    else:
      if input_down('frame-next') or input_down('frame-next-alt'):
        if play_direction == 1:
          speed_index += 1
        else:
          play_direction = -play_direction
      elif input_down('frame-prev') or input_down('frame-prev-alt'):
        if play_direction == -1:
          speed_index += 1
        else:
          play_direction = -play_direction
      elif input_down('frame-next-fast'):
        if play_direction == 1:
          speed_index += 2
        else:
          play_direction = -play_direction
          speed_index += 1
      elif input_down('frame-prev-fast'):
        if play_direction == -1:
          speed_index += 2
        else:
          play_direction = -play_direction
          speed_index += 1
      speed_index = min(max(speed_index, 0), len(speed_options) - 1)

    self.model.play_speed = play_direction * speed_options[speed_index]
    self.model.playback_mode = saved_play_direction.value != 0

    def play_button(label: str, direction: int) -> None:
      disabled = play_direction == direction
      if ig.disableable_button(label, enabled=play_direction != direction):
        saved_play_direction.value = direction

    play_button('<|', -1)
    ig.same_line()
    play_button('||', 0)
    ig.same_line()
    play_button('|>', 1)
    ig.same_line()

    ig.push_item_width(63)
    changed, new_index = ig.combo(
      '##speed-option',
      speed_index,
      [str(s) + 'x' for s in speed_options],
    )
    ig.pop_item_width()
    if changed:
      saved_speed_index.value = new_index

    if input_pressed('playback-play'):
      if saved_play_direction.value == 0:
        saved_play_direction.value = 1
      else:
        saved_play_direction.value = 0
    if input_pressed('playback-rewind'):
      if saved_play_direction.value == 0:
        saved_play_direction.value = -1
      else:
        saved_play_direction.value = 0
    if input_pressed('playback-speed-up'):
      saved_speed_index.value = min(saved_speed_index.value + 1, len(speed_options) - 1)
    if input_pressed('playback-slow-down'):
      saved_speed_index.value = max(saved_speed_index.value - 1, 0)


    ig.same_line()
    new_frame = ui.render_frame_slider(
      'frame-slider',
      self.model.selected_frame,
      self.model.max_frame - 1,
      self.model.pipeline.cached_frames() if self.show_debug_pane else [],
    )
    if new_frame is not None:
      self.model.selected_frame = new_frame.value
Exemplo n.º 6
0
    def render_frame_log_tab(self) -> None:
        frame_offset = use_state('frame-offset', 1)
        round_numbers = use_state('round-numbers', True)

        ig.push_item_width(210)
        _, frame_offset.value = ig.combo(
            '##frame-offset',
            frame_offset.value,
            ['previous -> current frame', 'current -> next frame'],
        )
        ig.pop_item_width()
        _, round_numbers.value = ig.checkbox('Round##round-numbers',
                                             round_numbers.value)
        ig.dummy(1, 10)

        events = self.model.pipeline.frame_log(self.model.selected_frame +
                                               frame_offset.value)

        def string(addr: object) -> str:
            return self.model.pipeline.read_string(0,
                                                   dcast(Address,
                                                         addr)).decode('utf-8')

        def f32(number: object) -> str:
            assert isinstance(number, float)
            if round_numbers.value:
                return '%.3f' % number
            else:
                return str(number)

        def vec3f(vector: object) -> str:
            return '(' + ', '.join(map(f32, dcast(list, vector))) + ')'

        def action(action: object) -> str:
            return self.model.action_names[dcast(int, action)]

        indent = 0
        action_indent = 0

        def show_text(text: str) -> None:
            ig.text('    ' * indent + text)

        for event in events:
            if event['type'] == 'FLT_CHANGE_ACTION':
                show_text(
                    f'change action: {action(event["from"])} -> {action(event["to"])}'
                )
            elif event['type'] == 'FLT_CHANGE_FORWARD_VEL':
                show_text(
                    f'change f vel: {f32(event["from"])} -> {f32(event["to"])} ({string(event["reason"])})'
                )
            elif event['type'] == 'FLT_WALL_PUSH':
                show_text(
                    f'wall push: {vec3f(event["from"])} -> {vec3f(event["to"])} (surface {event["surface"]})'
                )
            elif event['type'] == 'FLT_BEGIN_MOVEMENT_STEP':
                type_ = {1: 'air', 2: 'ground', 3: 'water'}[event['stepType']]
                show_text(f'{type_} step {event["stepNum"]}:')
                indent += 1
            elif event['type'] == 'FLT_END_MOVEMENT_STEP':
                indent -= 1
            elif event['type'] == 'FLT_EXECUTE_ACTION':
                indent -= action_indent
                action_indent = 0
                show_text(f'execute action: {action(event["action"])}')
                indent += 1
                action_indent += 1
            else:
                sorted_event = {'type': event['type']}
                sorted_event.update(sorted(event.items()))
                show_text(str(sorted_event))
Exemplo n.º 7
0
    def render_intended_stick_control(self, id: str) -> None:
        up_options = ['3d view', 'mario yaw', 'stick y', 'world x']
        up_option = use_state('up-option', 0)

        ig.text('up =')
        ig.same_line()
        ig.push_item_width(100)
        _, up_option.value = ig.combo('##up-option', up_option.value,
                                      up_options)
        ig.pop_item_width()
        ig.dummy(1, 10)

        stick_x_var = Variable('input-stick-x').with_frame(
            self.model.selected_frame)
        stick_y_var = Variable('input-stick-y').with_frame(
            self.model.selected_frame)

        face_yaw = dcast(
            int,
            self.model.get(
                Variable('mario-face-yaw').with_frame(
                    self.model.selected_frame)))
        camera_yaw = dcast(
            int,
            self.model.get(
                Variable('camera-yaw').with_frame(self.model.selected_frame))
            or 0)
        squish_timer = dcast(
            int,
            self.model.get(self.model.selected_frame,
                           'gMarioState->squishTimer'))
        active_face_yaw = face_yaw

        events = self.model.pipeline.frame_log(self.model.selected_frame + 1)

        active_face_yaw_action = None
        for event in events:
            if event['type'] == 'FLT_EXECUTE_ACTION':
                action_name = self.model.action_names[event['action']]
                active_face_yaw = event['faceAngle'][1]
                active_face_yaw_action = action_name
                if action_name == 'idle':
                    break

        up_angle = {
            'mario yaw': active_face_yaw,
            'stick y': camera_yaw + 0x8000,
            'world x': 0x4000,
            '3d view': self.model.rotational_camera_yaw,
        }[up_options[up_option.value]]
        self.model.input_up_yaw = up_angle

        raw_stick_x = dcast(int, self.model.get(stick_x_var))
        raw_stick_y = dcast(int, self.model.get(stick_y_var))

        adjusted = stick_raw_to_adjusted(raw_stick_x, raw_stick_y)
        intended = stick_adjusted_to_intended(
            adjusted,
            face_yaw,
            camera_yaw,
            squish_timer != 0,
        )

        def render_value(label: str, value: object,
                         formatter: VariableFormatter) -> Optional[Any]:
            label_width = 60
            value_size = (
                60 if label == 'dyaw' else 80,
                ig.get_text_line_height() +
                2 * ig.get_style().frame_padding[1],
            )
            ig.push_item_width(label_width)
            ig.selectable(label, width=label_width)
            ig.pop_item_width()
            ig.same_line()
            new_value, _, _ = ui.render_variable_value('value-' + label, value,
                                                       formatter, value_size)
            return None if new_value is None else new_value.value

        target_yaw: Optional[int] = None
        target_dyaw: Optional[int] = None
        target_mag: Optional[float] = None

        target_mag = render_value('int mag', intended.mag, FloatFormatter())
        target_yaw = render_value('int yaw', intended.yaw,
                                  DecimalIntFormatter())
        dyaw = intended.yaw - active_face_yaw
        target_dyaw = render_value('dyaw', dyaw, DecimalIntFormatter())

        ig.same_line()
        if ig.button('?'):
            ig.open_popup('active-yaw-expl')
        if ig.begin_popup('active-yaw-expl'):
            ig.text(f'{intended.yaw} - {active_face_yaw} = {dyaw}')
            ig.text(f'intended yaw = {intended.yaw}')
            if active_face_yaw == face_yaw:
                ig.text(f'face yaw = {face_yaw}')
            if active_face_yaw != face_yaw:
                ig.text(
                    f'face yaw = {active_face_yaw} at start of {active_face_yaw_action} action'
                )
                ig.text(f'(face yaw = {face_yaw} at start of frame)')
            ig.end_popup()

        if dyaw not in range(0, 16):
            if ig.button('dyaw = 0'):
                target_dyaw = 0

        if target_yaw is not None or target_dyaw is not None or target_mag is not None:
            relative_to = 0 if target_yaw is not None else active_face_yaw
            if target_dyaw is not None:
                target_yaw = active_face_yaw + target_dyaw
            if target_yaw is None:
                target_yaw = intended.yaw
            if target_mag is None:
                target_mag = intended.mag

            new_raw_stick_x, new_raw_stick_y = intended_to_raw(
                face_yaw, camera_yaw, squish_timer, target_yaw, target_mag,
                relative_to)

            self.model.set(stick_x_var, new_raw_stick_x)
            self.model.set(stick_y_var, new_raw_stick_y)

        n_a = intended.yaw - up_angle
        n_x = intended.mag / 32 * math.sin(-n_a * math.pi / 0x8000)
        n_y = intended.mag / 32 * math.cos(n_a * math.pi / 0x8000)

        ig.set_cursor_pos((ig.get_cursor_pos().x + 155, 0))
        new_n = ui.render_joystick_control(id, n_x, n_y, 'circle')

        if new_n is not None:
            new_n_a = int(math.atan2(-new_n[0], new_n[1]) * 0x8000 / math.pi)
            new_intended_yaw = up_angle + new_n_a
            new_intended_mag = 32 * math.sqrt(new_n[0]**2 + new_n[1]**2)

            new_raw_stick_x, new_raw_stick_y = intended_to_raw(
                face_yaw,
                camera_yaw,
                squish_timer,
                new_intended_yaw,
                new_intended_mag,
                relative_to=0)

            self.model.set(stick_x_var, new_raw_stick_x)
            self.model.set(stick_y_var, new_raw_stick_y)