Пример #1
0
def render_object_slots(
  id: str,
  behaviors: List[Optional[ObjectBehavior]],
  behavior_name: Callable[[ObjectBehavior], str],
) -> Optional[int]:
  ig.push_id(id)

  button_size = 50
  window_left = ig.get_window_position()[0]
  window_right = window_left + ig.get_window_content_region_max()[0]
  prev_item_right = window_left
  style = ig.get_style()

  result = None

  for slot, behavior in enumerate(behaviors):
    item_right = prev_item_right + style.item_spacing[0] + button_size
    if item_right > window_right:
      prev_item_right = window_left
    elif slot != 0:
      ig.same_line()
    prev_item_right = prev_item_right + style.item_spacing[0] + button_size

    if behavior is None:
      label = str(slot)
    else:
      label = str(slot) + '\n' + behavior_name(behavior)

    if ig.button(label + '##slot-' + str(slot), button_size, button_size):
      result = slot

  ig.pop_id()
  return result
Пример #2
0
def render_pos_y_slider(
    id: str,
    pos_y: float,
    mario_pos_y: float,
) -> Tuple[Optional[float], bool]:
    ig.push_id(id)

    ig.text('max y = %.f' % pos_y)
    ig.set_cursor_pos((ig.get_window_width() - 30, ig.get_cursor_pos().y))

    slider_pos = ig.get_cursor_pos()
    slider_width = 20
    slider_height = ig.get_content_region_available().y
    slider_range = range(-8191, 8192)

    mario_icon_x = ig.get_cursor_pos().x
    t = (mario_pos_y - slider_range.start) / len(slider_range)
    mario_icon_y = ig.get_cursor_pos().y + (1 - t) * slider_height
    ig.set_cursor_pos((mario_icon_x, mario_icon_y))
    reset = ig.button('M', width=slider_width)

    ig.set_cursor_pos(slider_pos)
    changed, value = ig.v_slider_float(
        '##slider',
        width=slider_width,
        height=slider_height,
        value=pos_y,
        min_value=slider_range.start,
        max_value=slider_range.stop - 1,
        format='',
    )
    new_y = value if changed else None

    ig.pop_id()
    return new_y, reset
Пример #3
0
    def render(id: str) -> None:
        nonlocal error

        if error is not None:
            message = error.strip()
            ig.text('Wafel has crashed. Cause:')
            lines = message.split('\n')
            ig.input_text_multiline(
                '##error-msg',
                message,
                len(message) + 1,
                max(map(len, lines)) * 10,
                (len(lines) + 1) * ig.get_text_line_height() + 6,
            )

            ig.dummy(10, 10)

            if ig.button('Exit'):
                log.info('Aborted')
                sys.exit(1)
            ig.same_line()
            if ig.button('Try to save'):
                if view.ask_save_filename():
                    view.save()
            # ig.same_line()
            # if view is not None and ig.button('Try to continue (mad lads only)'):
            #   view.reload_ui()
            #   error = None
            return

        try:
            if hasattr(model, 'pipeline'):
                log.timer.get_num_copies = lambda: model.pipeline.num_copies(
                ) if config.dev_mode else 0
                log.timer.get_num_updates = lambda: model.pipeline.num_advances(
                ) if config.dev_mode else 0

            log.timer.begin_frame()
            ig.try_render(lambda: do_render(id))
        except:
            error = traceback.format_exc()
            log.error('Caught: ' + error)
        finally:
            log.timer.end_frame()
Пример #4
0
def binding_button(name: str, label: str, width=0) -> None:
    listening_for: Ref[Optional[str]] = use_state('listening-for', None)

    controls: Ref[List[str]] = use_state('controls', [])
    controls.value.append(name)

    value = check_input(bindings.get(name))
    color = (0.26 + value * 0.7, 0.59 + value * 0.41, 0.98, 0.4)
    if listening_for.value == name:
        color = (0.86, 0.59, 0.98, 0.4)

    ig.push_style_color(ig.COLOR_BUTTON, *color)
    if ig.button(label, width=width):
        listening_for.value = name
    ig.pop_style_color()

    if ig.begin_popup_context_item('##btn-ctx-' + name):
        if ig.menu_item('Clear')[0] and name in bindings:
            del bindings[name]
        if ig.menu_item('Default')[0] and name in DEFAULT_BINDINGS:
            bindings[name] = DEFAULT_BINDINGS[name]
        ig.end_popup_context_item()

    ig.same_line()
    if name in bindings:
        text = str(bindings[name])
    else:
        text = 'Unbound'
    if listening_for.value == name:
        text = '(' + text + ')'

    if name in bindings:
        overlapping = find_overlapping_bindings().get(bindings[name])
    else:
        overlapping = None

    if overlapping is None:
        ig.text(text)
    else:
        ig.text_colored(text, 1.0, 0.8, 0.0, 1.0)
        if ig.is_item_hovered():

            def control_name(name: str) -> str:
                return name.replace('-', ' ').replace('n64', 'N64')

            overlapping = [c for c in overlapping if c != name]
            ig.set_tooltip('Also bound to ' +
                           ', '.join(map(control_name, overlapping)))
Пример #5
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)
Пример #6
0
def render_joystick_control(
    id: str,
    stick_x: float,
    stick_y: float,
    shape='square',
) -> Optional[Tuple[float, float]]:
    ig.push_id(id)
    state = use_state('', JoystickControlState()).value

    dl = ig.get_window_draw_list()

    padding = 10
    content_region = ig.get_content_region_available()
    size = min(
        content_region.x - ig.get_style().scrollbar_size - 2 * padding,
        content_region.y - 2 * padding,
        200,
    )
    size = max(size, 100)

    initial_cursor_pos = ig.get_cursor_pos()
    top_left = (
        initial_cursor_pos[0] + ig.get_window_position()[0] -
        ig.get_scroll_x() + padding,
        initial_cursor_pos[1] + ig.get_window_position()[1] -
        ig.get_scroll_y() + padding,
    )

    background_color = ig.get_color_u32_rgba(0, 0, 0, 0.3)
    if shape == 'square':
        dl.add_rect_filled(
            top_left[0],
            top_left[1],
            top_left[0] + size,
            top_left[1] + size,
            background_color,
        )
    elif shape == 'circle':
        dl.add_circle_filled(
            top_left[0] + size / 2,
            top_left[1] + size / 2,
            size / 2,
            background_color,
            num_segments=32,
        )

    result = None

    if state.active and ig.is_mouse_down():
        new_offset = state.get_value(ig.get_mouse_drag_delta(lock_threshold=0))

        new_stick_x = new_offset[0] / size * 2 - 1
        new_stick_y = (1 - new_offset[1] / size) * 2 - 1
        if shape == 'square':
            new_stick_x = min(max(new_stick_x, -1), 1)
            new_stick_y = min(max(new_stick_y, -1), 1)
        elif shape == 'circle':
            mag = math.sqrt(new_stick_x**2 + new_stick_y**2)
            if mag > 1:
                new_stick_x /= mag
                new_stick_y /= mag

        if (new_stick_x, new_stick_y) != (stick_x, stick_y):
            stick_x, stick_y = new_stick_x, new_stick_y
            result = (stick_x, stick_y)

    offset = (
        (stick_x + 1) / 2 * size,
        (1 - (stick_y + 1) / 2) * size,
    )

    dl.add_line(
        top_left[0] + size / 2,
        top_left[1] + size / 2,
        top_left[0] + offset[0],
        top_left[1] + offset[1],
        ig.get_color_u32_rgba(1, 1, 1, 0.5),
    )

    button_size = 20
    button_pos = (
        padding + initial_cursor_pos[0] + offset[0] - button_size / 2,
        padding + initial_cursor_pos[1] + offset[1] - button_size / 2,
    )
    ig.set_cursor_pos(button_pos)
    ig.button('##joystick-button', button_size, button_size)

    ig.set_cursor_pos((
        initial_cursor_pos[0],
        initial_cursor_pos[1] + size + 2 * padding,
    ))

    if ig.is_item_active():
        state.set_active(offset)
    else:
        state.reset()

    ig.pop_id()
    return result