diff --git a/include/parts.h b/include/parts.h index e2dcca4..b589e05 100644 --- a/include/parts.h +++ b/include/parts.h @@ -172,6 +172,7 @@ void PE_SetPassCursor(int parts_no, bool pass); bool PE_GetPartsPassCursor(int parts_no); void PE_SetClickable(int parts_no, bool clickable); bool PE_GetPartsClickable(int parts_no); +void PE_SetDrag(int parts_no, bool enable); void PE_SetPartsGroupDecideOnCursor(int group_no, bool decide_on_cursor); void PE_SetPartsGroupDecideClick(int group_no, bool decide_click); void PE_SetOnCursorShowLinkPartsNumber(int parts_no, int link_parts_no); diff --git a/src/hll/PartsEngine.c b/src/hll/PartsEngine.c index 7e0df7d..7cd96b2 100644 --- a/src/hll/PartsEngine.c +++ b/src/hll/PartsEngine.c @@ -837,7 +837,7 @@ HLL_LIBRARY(PartsEngine, HLL_EXPORT(Parts_SetPassCursor, PE_SetPassCursor), HLL_EXPORT(Parts_SetClickable, PE_SetClickable), HLL_TODO_EXPORT(Parts_SetResetTimerByChangeInputStatus, PartsEngine_Parts_SetResetTimerByChangeInputStatus), - HLL_TODO_EXPORT(Parts_SetDrag, PartsEngine_Parts_SetDrag), + HLL_EXPORT(Parts_SetDrag, PE_SetDrag), HLL_EXPORT(Parts_SetParentPartsNumber, PE_SetParentPartsNumber), HLL_EXPORT(Parts_SetInputState, PE_SetInputState), HLL_EXPORT(Parts_SetOnCursorShowLinkPartsNumber, PE_SetOnCursorShowLinkPartsNumber), diff --git a/src/parts/input.c b/src/parts/input.c index 35555dd..2ddeda3 100644 --- a/src/parts/input.c +++ b/src/parts/input.c @@ -34,6 +34,13 @@ static int clicked_parts = 0; // the current (partially) clicked parts number static int click_down_parts = 0; +// drag state +static struct parts *drag_parts = NULL; +static bool is_dragging = false; +static Point drag_initial_pos; +static Point drag_start_cursor; +static struct parts *drop_target = NULL; + static bool parts_hittest(struct parts *parts, int state, Point pos) { Rectangle hitbox = parts->states[state].common.hitbox; @@ -44,6 +51,21 @@ static bool parts_hittest(struct parts *parts, int state, Point pos) return SDL_PointInRect(&pos, &hitbox); } +static void drag_state_reset(void) +{ + drag_parts = NULL; + is_dragging = false; + drop_target = NULL; +} + +void parts_input_reset_drag(struct parts *parts) +{ + if (parts == drag_parts) + drag_state_reset(); + else if (parts == drop_target) + drop_target = NULL; +} + static void parts_update_mouse(struct parts *parts, Point cur_pos, bool cur_clicking, int passed_time, bool *hover_consumed, bool *click_consumed) { @@ -66,25 +88,22 @@ static void parts_update_mouse(struct parts *parts, Point cur_pos, bool cur_clic return; if (is_hovered && !was_hovered) { - parts_msg_push(parts->no, parts->delegate_index, - PARTS_MSG_MOUSE_ENTER, "ii", cur_pos.x, cur_pos.y); + parts_msg_push(parts, PARTS_MSG_MOUSE_ENTER, "ii", cur_pos.x, cur_pos.y); parts->hover_time = 0; } if (!is_hovered && was_hovered) { - parts_msg_push(parts->no, parts->delegate_index, - PARTS_MSG_MOUSE_LEAVE, "ii", cur_pos.x, cur_pos.y); + parts_msg_push(parts, PARTS_MSG_MOUSE_LEAVE, "ii", cur_pos.x, cur_pos.y); } if (is_hovered) { // MOUSE_ON message fires every frame while hovering - parts_msg_push(parts->no, parts->delegate_index, - PARTS_MSG_MOUSE_ON, "iii", cur_pos.x, cur_pos.y, parts->hover_time); + parts_msg_push(parts, PARTS_MSG_MOUSE_ON, + "iii", cur_pos.x, cur_pos.y, parts->hover_time); parts->hover_time += passed_time; // MOUSE_MOVE message fires when cursor moves while hovering if (cur_pos.x != parts_prev_pos.x || cur_pos.y != parts_prev_pos.y) { - parts_msg_push(parts->no, parts->delegate_index, - PARTS_MSG_MOUSE_MOVE, "ii", cur_pos.x, cur_pos.y); + parts_msg_push(parts, PARTS_MSG_MOUSE_MOVE, "ii", cur_pos.x, cur_pos.y); } } @@ -106,15 +125,17 @@ static void parts_update_mouse(struct parts *parts, Point cur_pos, bool cur_clic click_down_parts = parts->no; *click_consumed = true; + drag_parts = parts; + drag_initial_pos = parts->local.pos; + drag_start_cursor = cur_pos; + // KEY_TRIGGER message fires on press transition - parts_msg_push(parts->no, parts->delegate_index, - PARTS_MSG_KEY_TRIGGER, "i", VK_LBUTTON); + parts_msg_push(parts, PARTS_MSG_KEY_TRIGGER, "i", VK_LBUTTON); } // KEY_DOWN message fires every frame while held (not first frame) if (prev_clicking && cur_clicking && click_down_parts == parts->no) { - parts_msg_push(parts->no, parts->delegate_index, - PARTS_MSG_KEY_DOWN, "i", VK_LBUTTON); + parts_msg_push(parts, PARTS_MSG_KEY_DOWN, "i", VK_LBUTTON); } if (cur_clicking && click_down_parts == parts->no) { @@ -132,10 +153,9 @@ static void parts_update_mouse(struct parts *parts, Point cur_pos, bool cur_clic audio_play_sound(parts->on_click_sound); clicked_parts = parts->no; - parts_msg_push(parts->no, parts->delegate_index, - PARTS_MSG_MOUSE_CLICK, "iii", cur_pos.x, cur_pos.y, VK_LBUTTON); - parts_msg_push(parts->no, parts->delegate_index, - PARTS_MSG_KEY_UP, "i", VK_LBUTTON); + parts_msg_push(parts, PARTS_MSG_MOUSE_CLICK, + "iii", cur_pos.x, cur_pos.y, VK_LBUTTON); + parts_msg_push(parts, PARTS_MSG_KEY_UP, "i", VK_LBUTTON); } } @@ -154,7 +174,65 @@ void PE_UpdateInputState(int passed_time) &hover_consumed, &click_consumed); } + // Drag movement processing + if (drag_parts && cur_clicking) { + bool cursor_moved = (cur_pos.x != parts_prev_pos.x || + cur_pos.y != parts_prev_pos.y); + if (cursor_moved && drag_parts->draggable) { + Point new_pos = { + drag_initial_pos.x + (cur_pos.x - drag_start_cursor.x), + drag_initial_pos.y + (cur_pos.y - drag_start_cursor.y) + }; + parts_set_pos(drag_parts, new_pos); + if (!is_dragging) { + parts_msg_push(drag_parts, PARTS_MSG_DRAG_BEGIN, ""); + } + parts_msg_push(drag_parts, PARTS_MSG_DRAGGING, "iiii", + drag_start_cursor.x, drag_start_cursor.y, cur_pos.x, cur_pos.y); + is_dragging = true; + } + + // Drop target tracking + if (cursor_moved && is_dragging) { + struct parts *new_drop = NULL; + PARTS_LIST_FOREACH_REVERSE(parts) { + if (parts == drag_parts) + continue; + if (parts_hittest(parts, PARTS_STATE_DEFAULT, cur_pos)) { + new_drop = parts; + if (!parts->pass_cursor) + break; + } + } + if (new_drop != drop_target) { + if (drop_target) { + parts_msg_push(drop_target, PARTS_MSG_DROP_LEAVE, + "i", drag_parts->no); + } + if (new_drop) { + parts_msg_push(new_drop, PARTS_MSG_DROP_ENTER, + "i", drag_parts->no); + } + drop_target = new_drop; + } else if (drop_target) { + parts_msg_push(drop_target, PARTS_MSG_DROP_ON, + "iii", drag_parts->no, cur_pos.x, cur_pos.y); + } + } + } + + // Release handling if (prev_clicking && !cur_clicking) { + if (is_dragging && drag_parts && drag_parts->draggable) { + parts_msg_push(drag_parts, PARTS_MSG_DRAG_END, ""); + } + if (drop_target && drag_parts) { + parts_msg_push(drop_target, PARTS_MSG_DROPPED, + "iii", drag_parts->no, cur_pos.x, cur_pos.y); + parts_msg_push(drop_target, PARTS_MSG_DROP_LEAVE, "i", drag_parts->no); + } + drag_state_reset(); + if (!click_down_parts) { // TODO: play misclick sound } @@ -247,6 +325,12 @@ void PE_EndInput(void) { parts_began_click = false; clicked_parts = 0; + drag_state_reset(); +} + +void PE_SetDrag(int parts_no, bool enable) +{ + parts_get(parts_no)->draggable = !!enable; } int PE_GetClickPartsNumber(void) diff --git a/src/parts/message.c b/src/parts/message.c index 2bc22bd..0c748c5 100644 --- a/src/parts/message.c +++ b/src/parts/message.c @@ -39,15 +39,15 @@ struct parts_message { static STAILQ_HEAD(, parts_message) msg_queue = STAILQ_HEAD_INITIALIZER(msg_queue); -void parts_msg_push(int parts_no, int delegate_index, int type, const char *fmt, ...) +void parts_msg_push(struct parts* parts, int type, const char *fmt, ...) { // Message system is introduced in Rance 9 if (!parts_multi_controller) return; struct parts_message *msg = xmalloc(sizeof(*msg)); - msg->parts_no = parts_no; - msg->delegate_index = delegate_index; + msg->parts_no = parts->no; + msg->delegate_index = parts->delegate_index; msg->type = type; msg->nr_variables = 0; diff --git a/src/parts/parts.c b/src/parts/parts.c index 196dba4..293da01 100644 --- a/src/parts/parts.c +++ b/src/parts/parts.c @@ -970,6 +970,7 @@ void parts_release(int parts_no) return; struct parts *parts = slot->value; + parts_input_reset_drag(parts); parts_clear_motion(parts); for (int i = 0; i < PARTS_NR_STATES; i++) { parts_state_free(&parts->states[i]); diff --git a/src/parts/parts_internal.h b/src/parts/parts_internal.h index c073d9d..99d9465 100644 --- a/src/parts/parts_internal.h +++ b/src/parts/parts_internal.h @@ -393,6 +393,7 @@ struct parts { bool pass_cursor; bool lock_input_state; bool want_save; + bool draggable; int on_cursor_sound; int on_click_sound; int origin_mode; @@ -500,6 +501,7 @@ void parts_add_motion(struct parts *parts, struct parts_motion *motion); // input.c extern bool parts_began_click; +void parts_input_reset_drag(struct parts *parts); // message.c enum parts_message_type { @@ -512,12 +514,16 @@ enum parts_message_type { PARTS_MSG_DRAG_BEGIN = 7, PARTS_MSG_DRAGGING = 8, PARTS_MSG_DRAG_END = 9, + PARTS_MSG_DROP_ENTER = 10, + PARTS_MSG_DROP_ON = 11, + PARTS_MSG_DROPPED = 12, + PARTS_MSG_DROP_LEAVE = 13, PARTS_MSG_KEY_TRIGGER = 14, PARTS_MSG_KEY_DOWN = 15, PARTS_MSG_KEY_UP = 17, }; -void parts_msg_push(int parts_no, int delegate_index, int type, const char *fmt, ...); +void parts_msg_push(struct parts* parts, int type, const char *fmt, ...); // construction.c void parts_cp_op_free(struct parts_cp_op *op); diff --git a/src/parts/save.c b/src/parts/save.c index 71e6d5d..6f5c8cb 100644 --- a/src/parts/save.c +++ b/src/parts/save.c @@ -570,6 +570,7 @@ static void save_parts(struct iarray_writer *w, struct parts *parts) iarray_write(w, parts->margin_bottom); iarray_write(w, parts->margin_left); iarray_write(w, parts->margin_right); + iarray_write(w, parts->draggable); } unsigned motion_count_pos = iarray_writer_pos(w); @@ -620,6 +621,7 @@ static void load_parts(struct iarray_reader *r, int version) parts->margin_bottom = iarray_read(r); parts->margin_left = iarray_read(r); parts->margin_right = iarray_read(r); + parts->draggable = iarray_read(r); } int motion_count = iarray_read(r);