From ebb19555003bd21605a96745d6ab58be203ebd72 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Sun, 2 Jun 2024 10:13:47 -0500 Subject: [PATCH] chess: cursor axis inversion, initial "animator" concept --- chess/chess.cpp | 12 +++++++++++- chess/main.cpp | 36 +++++++++++++++++++++++++++++++----- chess/render.cpp | 2 +- chess/render.hpp | 34 +++++++++++++++++++++++++++++++++- maple/maple_bus_ft9.hpp | 4 ++-- regs/maple_bus_ft9.csv | 2 +- regs/maple_bus_ft9.ods | Bin 12848 -> 12860 bytes 7 files changed, 79 insertions(+), 11 deletions(-) diff --git a/chess/chess.cpp b/chess/chess.cpp index 18e0ff3..9ddc86b 100644 --- a/chess/chess.cpp +++ b/chess/chess.cpp @@ -2,6 +2,7 @@ #include "chess.hpp" #include "sh7091/serial.hpp" + namespace chess { xy position_to_xy(int8_t position) @@ -281,7 +282,7 @@ static void board_init(game_state& game_state) void game_init(game_state& game_state) { - game_state.turn = -1; // white + game_state.turn = 1; // white game_state.en_passant_target = -1; game_state.halfmove_number = 0; game_state.fullmove_number = 0; @@ -452,6 +453,14 @@ void clear_annotations(game_state& game_state) game_state.interaction.annotation_list.length = 0; } +void delete_annotation(annotation_list& annotation_list, int i) +{ + annotation_list.length -= 1; + for (int j = i; j < annotation_list.length; j++) { + annotation_list.annotation[j] = annotation_list.annotation[j + 1]; + } +} + void annotate_position(game_state& game_state, int8_t x, int8_t y) { int8_t position = xy_to_position(x, y); @@ -459,6 +468,7 @@ void annotate_position(game_state& game_state, int8_t x, int8_t y) for (int i = 0; i < annotation_list.length; i++) { if (annotation_list.annotation[i].from_position == position) { + delete_annotation(annotation_list, i); return; } } diff --git a/chess/main.cpp b/chess/main.cpp index 9046576..69a419e 100644 --- a/chess/main.cpp +++ b/chess/main.cpp @@ -34,16 +34,23 @@ void cursor_update(struct render::cursor_state& cursor_state, uint32_t frame_ix) continue; auto& cursor = cursor_state.cur[cursor_ix]; + float invert = port_ix % 2 == 0 ? 1.f : -1.f; + auto& port = input::state.port[port_ix]; if (port.function_type & function_type::controller) { auto& bus_data = port.host_response_data_transfer_ft0->bus_data; auto& data_fields = bus_data.data_fields; if (std::byteswap(data_fields.function_type) & function_type::controller) { auto& data = data_fields.data; - cursor.x += static_cast(data.analog_coordinate_axis[2] - 0x80) * 0.0035; - cursor.y += static_cast(data.analog_coordinate_axis[3] - 0x80) * -0.0035; + cursor.x += static_cast(data.analog_coordinate_axis[2] - 0x80) * 0.0015 * invert; + cursor.y += static_cast(data.analog_coordinate_axis[3] - 0x80) * -0.0015 * invert; cursor.button[frame_ix].a = ft0::data_transfer::digital_button::a(data.digital_button) == 0; cursor.button[frame_ix].b = ft0::data_transfer::digital_button::b(data.digital_button) == 0; + bool start = ft0::data_transfer::digital_button::start(data.digital_button) == 0; + if (start) { + cursor.x = 3.5f; + cursor.y = 3.5f; + } } } if (port.function_type & function_type::pointing) { @@ -51,12 +58,22 @@ void cursor_update(struct render::cursor_state& cursor_state, uint32_t frame_ix) auto& data_fields = bus_data.data_fields; if (std::byteswap(data_fields.function_type) & function_type::pointing) { auto& data = data_fields.data; - cursor.x += static_cast(data.analog_coordinate_axis[0] - 0x200) * 0.0035; - cursor.y += static_cast(data.analog_coordinate_axis[1] - 0x200) * -0.0035; + cursor.x += static_cast(data.analog_coordinate_axis[0] - 0x200) * 0.0065 * invert; + cursor.y += static_cast(data.analog_coordinate_axis[1] - 0x200) * -0.0065 * invert; cursor.button[frame_ix].a = ft9::data_transfer::digital_button::a(data.digital_button) == 0; cursor.button[frame_ix].b = ft9::data_transfer::digital_button::b(data.digital_button) == 0; + bool w = ft9::data_transfer::digital_button::w(data.digital_button) == 0; + if (w) { + cursor.x = 3.5f; + cursor.y = 3.5f; + } } } + + if (cursor.x < -1.0f) cursor.x = -1.0f; + if (cursor.x > 8.0f) cursor.x = 8.0f; + if (cursor.y < -0.5f) cursor.y = -0.5f; + if (cursor.y > 7.5f) cursor.y = 7.5f; } } @@ -100,7 +117,7 @@ void main() core_init(); region_array2(640 / 32, 480 / 32, opb_size); - background_parameter(0xff0000ff); + background_parameter(0xff223311); chess::game_state game_state; chess::game_init(game_state); @@ -116,13 +133,22 @@ void main() vt.piece_rotation = 0.f; vt.board_rotation = 0.f; + auto piece_rotation_animator = render::animator(0.f); + struct render::cursor_state cursor_state = render::cursor_state(); + constexpr float pi = 3.141592653589793f; + while (true) { input::state_update(send_buf, recv_buf); cursor_update(cursor_state, frame_ix); cursor_events(game_state, cursor_state, frame_ix); + float target = game_state.turn == 1 ? 0.f : pi; + piece_rotation_animator.set_target(target, 60); + //vt.board_rotation = piece_rotation_animator.interpolate(); + //vt.piece_rotation = piece_rotation_animator.interpolate(); + ta_polygon_converter_init(opb_size.total(), ta_alloc, 640 / 32, diff --git a/chess/render.cpp b/chess/render.cpp index d063304..1efbbba 100644 --- a/chess/render.cpp +++ b/chess/render.cpp @@ -282,8 +282,8 @@ void draw_pieces(const view_transform vt, uint32_t cursor_colors[4] = { 0xffff00ff, + 0xff0000ff, 0xff00ff00, - 0xffffff00, 0xffff0000, }; diff --git a/chess/render.hpp b/chess/render.hpp index 184fc26..ec58b64 100644 --- a/chess/render.hpp +++ b/chess/render.hpp @@ -11,6 +11,38 @@ struct view_transform { float board_rotation; }; +template +struct animator { + T start; + T end; + int steps; + int step; + + constexpr animator(T initial) + : start(initial), end(initial), steps(0), step(0) + { } + + void set_target(T target_value, int target_steps) + { + if (target_value != end) { + start = interpolate(); + end = target_value; + steps = target_steps; + step = 0; + } + } + + T interpolate() + { + if (step == steps) + return end; + + T value = start + ((end - start) * step) / steps; + step += 1; + return value; + } +}; + struct button { bool a; bool b; @@ -22,7 +54,7 @@ struct cursor { struct button button[2]; constexpr cursor() - : x(4.f), y(4.f) + : x(3.5f), y(3.5f) { button[0] = {false, false}; button[1] = {false, false}; diff --git a/maple/maple_bus_ft9.hpp b/maple/maple_bus_ft9.hpp index d586610..3fcfb6d 100644 --- a/maple/maple_bus_ft9.hpp +++ b/maple/maple_bus_ft9.hpp @@ -15,8 +15,8 @@ namespace ft9 { constexpr uint32_t u() { return 0b1 << 4; } constexpr uint32_t u(uint32_t reg) { return (reg >> 4) & 0b1; } - constexpr uint32_t s() { return 0b1 << 3; } - constexpr uint32_t s(uint32_t reg) { return (reg >> 3) & 0b1; } + constexpr uint32_t w() { return 0b1 << 3; } + constexpr uint32_t w(uint32_t reg) { return (reg >> 3) & 0b1; } constexpr uint32_t a() { return 0b1 << 2; } constexpr uint32_t a(uint32_t reg) { return (reg >> 2) & 0b1; } diff --git a/regs/maple_bus_ft9.csv b/regs/maple_bus_ft9.csv index 78ad170..8dbee27 100644 --- a/regs/maple_bus_ft9.csv +++ b/regs/maple_bus_ft9.csv @@ -1,5 +1,5 @@ "data_transfer",7,6,5,4,3,2,1,0 -"digital_button","r","l","d","u","s","a","b","c" +"digital_button","r","l","d","u","w","a","b","c" "option",,,,,,,"batt","wire" "analog_coordinate_overflow",,,,,,,, "reserved",,,,,,,, diff --git a/regs/maple_bus_ft9.ods b/regs/maple_bus_ft9.ods index 78d403828779210372d9f8adc3957eee473e5e42..909004b7f66e25b5c900d563bd235c15b9a685f3 100644 GIT binary patch delta 5705 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