From 45844b71aec59714e1d985a64aa06fed68daf12b Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Mon, 4 Nov 2019 02:13:00 +0100 Subject: [PATCH] Make controls more responsive - Fix double events - Fix ignored events - Allow moving diagonal in the inventory - Fix mouse wobbling in inventory when scalling - Make controler actions cursor independants - Make sure secoundery and primery key doesn't fire each others events - Highlight both primary and secondary target - Automatic switch between controller and keyboard+mouse - Allow the user to change facing direction when blocked - Make code event based instead of relying on time outs --- CMakeLists.txt | 1 - Source/cursor.cpp | 6 - Source/diablo.cpp | 6 + Source/inv.cpp | 3 +- Source/scrollrt.cpp | 6 +- SourceS/miniwin/misc.h | 2 +- SourceX/DiabloUI/diabloui.cpp | 3 + SourceX/controls/controller.cpp | 3 - SourceX/controls/game_controls.cpp | 3 + SourceX/controls/menu_controls.cpp | 3 + SourceX/controls/plrctrls.cpp | 376 +++++++++++------------------ SourceX/controls/plrctrls.h | 11 +- SourceX/miniwin/misc_dx.cpp | 32 --- SourceX/miniwin/misc_msg.cpp | 90 ++++--- 14 files changed, 231 insertions(+), 314 deletions(-) delete mode 100644 SourceX/miniwin/misc_dx.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cad5c4d1a..4e7300c6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -220,7 +220,6 @@ set(devilutionx_SRCS SourceX/miniwin/misc.cpp SourceX/miniwin/misc_io.cpp SourceX/miniwin/misc_msg.cpp - SourceX/miniwin/misc_dx.cpp SourceX/miniwin/rand.cpp SourceX/miniwin/thread.cpp SourceX/miniwin/dsound.cpp diff --git a/Source/cursor.cpp b/Source/cursor.cpp index 28a7cb8a8..92af07765 100644 --- a/Source/cursor.cpp +++ b/Source/cursor.cpp @@ -170,10 +170,6 @@ void CheckRportal() } } -// Controller support: Actions to run after updating the cursor state. -// Defined in SourceX/controls/plctrls.cpp. -extern void plrctrls_after_check_curs_move(); - void CheckCursMove() { int i, sx, sy, mx, my, tx, ty, px, py, xx, yy, mi; @@ -604,8 +600,6 @@ void CheckCursMove() if (pcursmonst != -1 && monster[pcursmonst]._mFlags & MFLAG_GOLEM) { pcursmonst = -1; } - - plrctrls_after_check_curs_move(); } DEVILUTION_END_NAMESPACE diff --git a/Source/diablo.cpp b/Source/diablo.cpp index 66ea39285..8fa3799f2 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -119,6 +119,10 @@ BOOL StartGame(BOOL bNewGame, BOOL bSinglePlayer) return gbRunGameResult; } +// Controller support: Actions to run after updating the cursor state. +// Defined in SourceX/controls/plctrls.cpp. +extern void plrctrls_after_check_curs_move(); + static void ProcessInput() { if (PauseMode == 2) { @@ -131,6 +135,7 @@ static void ProcessInput() if (!gmenu_exception() && sgnTimeoutCurs == 0) { CheckCursMove(); + plrctrls_after_check_curs_move(); track_process(); } } @@ -1694,6 +1699,7 @@ void game_logic() if (!gmenu_exception() && sgnTimeoutCurs == 0) { CheckCursMove(); + plrctrls_after_check_curs_move(); track_process(); } if (gbProcessPlayers) { diff --git a/Source/inv.cpp b/Source/inv.cpp index 5c61ad022..7da5d7f7a 100644 --- a/Source/inv.cpp +++ b/Source/inv.cpp @@ -405,7 +405,8 @@ void DrawInvBelt() colour = ICOL_BLUE; if (!plr[myplr].SpdList[i]._iStatFlag) colour = ICOL_RED; - CelBlitOutline(colour, InvRect[i + SLOTXY_BELT_FIRST].X + SCREEN_X, InvRect[i + SLOTXY_BELT_FIRST].Y + SCREEN_Y - 1, pCursCels, frame, frame_width); + if (!sgbControllerActive || invflag) + CelBlitOutline(colour, InvRect[i + SLOTXY_BELT_FIRST].X + SCREEN_X, InvRect[i + SLOTXY_BELT_FIRST].Y + SCREEN_Y - 1, pCursCels, frame, frame_width); } if (plr[myplr].SpdList[i]._iStatFlag) diff --git a/Source/scrollrt.cpp b/Source/scrollrt.cpp index 5a422f4b9..fb6f69382 100644 --- a/Source/scrollrt.cpp +++ b/Source/scrollrt.cpp @@ -102,7 +102,11 @@ static void scrollrt_draw_cursor_item() assert(! sgdwCursWdt); - if (pcurs <= 0 || cursW == 0 || cursH == 0 || (sgbControllerActive && !invflag && !chrflag)) { + if (pcurs <= 0 || cursW == 0 || cursH == 0) { + return; + } + + if (sgbControllerActive && !invflag && (!chrflag || plr[myplr]._pStatPts <= 0)) { return; } diff --git a/SourceS/miniwin/misc.h b/SourceS/miniwin/misc.h index 7710dd048..e75d45344 100644 --- a/SourceS/miniwin/misc.h +++ b/SourceS/miniwin/misc.h @@ -190,7 +190,7 @@ void SetEvent(HANDLE hEvent); void ResetEvent(HANDLE hEvent); int WINAPI WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); -WINBOOL WINAPI SetCursorPos(int X, int Y); +void SetCursorPos(int X, int Y); SHORT WINAPI GetAsyncKeyState(int vKey); diff --git a/SourceX/DiabloUI/diabloui.cpp b/SourceX/DiabloUI/diabloui.cpp index 6dee45478..4bca28986 100644 --- a/SourceX/DiabloUI/diabloui.cpp +++ b/SourceX/DiabloUI/diabloui.cpp @@ -778,6 +778,9 @@ bool UiItemMouseEvents(SDL_Event *event, UiItem *items, std::size_t size) void DrawMouse() { + if (sgbControllerActive) + return; + SDL_GetMouseState(&MouseX, &MouseY); #ifndef USE_SDL1 diff --git a/SourceX/controls/controller.cpp b/SourceX/controls/controller.cpp index 4a3eb4779..2dc7b5e6d 100644 --- a/SourceX/controls/controller.cpp +++ b/SourceX/controls/controller.cpp @@ -11,9 +11,6 @@ ControllerButtonEvent ToControllerButtonEvent(const SDL_Event &event) ControllerButtonEvent result{ ControllerButton::NONE, false }; switch (event.type) { #ifndef USE_SDL1 - case SDL_CONTROLLERAXISMOTION: - result.up = event.caxis.value == 0; - break; case SDL_CONTROLLERBUTTONUP: #endif case SDL_JOYBUTTONUP: diff --git a/SourceX/controls/game_controls.cpp b/SourceX/controls/game_controls.cpp index 1f4011d61..3e1573b92 100644 --- a/SourceX/controls/game_controls.cpp +++ b/SourceX/controls/game_controls.cpp @@ -117,6 +117,9 @@ bool GetGameAction(const SDL_Event &event, GameAction *action) if (CurrentGameController() != nullptr && event.type >= SDL_JOYAXISMOTION && event.type <= SDL_JOYBUTTONUP) { return true; } + if (event.type == SDL_CONTROLLERAXISMOTION) { + return true; // Ignore releasing the trigger buttons + } #endif return false; diff --git a/SourceX/controls/menu_controls.cpp b/SourceX/controls/menu_controls.cpp index 4ac0424f5..fe8bad3e1 100644 --- a/SourceX/controls/menu_controls.cpp +++ b/SourceX/controls/menu_controls.cpp @@ -7,6 +7,7 @@ namespace dvl { MenuAction GetMenuAction(const SDL_Event &event) { const ControllerButtonEvent ctrl_event = ToControllerButtonEvent(event); + sgbControllerActive = true; if (!ctrl_event.up) { switch (ctrl_event.button) { case ControllerButton::BUTTON_B: // Right button @@ -33,6 +34,8 @@ MenuAction GetMenuAction(const SDL_Event &event) break; } } + if (event.type < SDL_JOYAXISMOTION || event.type >= 0x700) + sgbControllerActive = false; #if HAS_KBCTRL == 0 if (event.type == SDL_KEYDOWN) { diff --git a/SourceX/controls/plrctrls.cpp b/SourceX/controls/plrctrls.cpp index 74fe414f8..8f94ea4fe 100644 --- a/SourceX/controls/plrctrls.cpp +++ b/SourceX/controls/plrctrls.cpp @@ -11,6 +11,7 @@ namespace dvl { bool sgbControllerActive = false; coords speedspellscoords[50]; +const int repeatRate = 100; int speedspellcount = 0; // Native game menu, controlled by simulating a keyboard. @@ -26,14 +27,8 @@ int hsr[3] = { 0, 0, 0 }; // hot spell row counts int slot = SLOTXY_INV_FIRST; int spbslot = 0; -// Menu controlled by simulating a mouse. -bool InControlledMenu() -{ - return invflag || spselflag || chrflag; -} - // 0 = not near, >0 = distance related player 1 coordinates -coords checkNearbyObjs(int x, int y, int diff) +coords CheckNearbyObjs(int x, int y, int diff) { int diff_x = abs(plr[myplr]._px - x); int diff_y = abs(plr[myplr]._py - y); @@ -47,119 +42,85 @@ coords checkNearbyObjs(int x, int y, int diff) return { -1, -1 }; } -void checkItemsNearby(bool interact) +void CheckItemsNearby() { for (int i = 0; i < MAXITEMS; i++) { - if (checkNearbyObjs(item[i]._ix, item[i]._iy, 1).x != -1 && item[i]._iSelFlag > 0 && item[i]._itype > -1) { - pcursitem = i; + if (CheckNearbyObjs(item[i]._ix, item[i]._iy, 1).x != -1 && item[i]._iSelFlag > 0 && item[i]._itype > -1) { if (dItem[item[i]._ix][item[i]._iy] <= 0) continue; - if (interact) { - //sprintf(tempstr, "FOUND NEARBY ITEM AT X:%i Y:%i SEL:%i", item[i]._ix, item[i]._iy, item[i]._iSelFlag); - //NetSendCmdString(1 << myplr, tempstr); - LeftMouseCmd(false); - } + pcursitem = i; return; // item nearby, don't find objects } } - if (sgbControllerActive) - pcursitem = -1; - //sprintf(tempstr, "SCANNING FOR OBJECTS"); - //NetSendCmdString(1 << myplr, tempstr); + for (int i = 0; i < MAXOBJECTS; i++) { - if (checkNearbyObjs(object[i]._ox, object[i]._oy, 1).x != -1 && object[i]._oSelFlag > 0 && object[i]._otype > -1 && currlevel) { // make sure we're in the dungeon to scan for objs + if (CheckNearbyObjs(object[i]._ox, object[i]._oy, 1).x != -1 && object[i]._oSelFlag > 0 && object[i]._otype > -1 && currlevel) { // make sure we're in the dungeon to scan for objs pcursobj = i; - if (interact) { - LeftMouseCmd(false); - } return; } } - if (sgbControllerActive) - pcursobj = -1; } -void checkTownersNearby(bool interact) +void CheckTownersNearby() { - if (pcursitem != -1) - // Items take priority over towners because the player can move - // items but not towners. - return; for (int i = 0; i < 16; i++) { - if (checkNearbyObjs(towner[i]._tx, towner[i]._ty, 2).x != -1) { + if (CheckNearbyObjs(towner[i]._tx, towner[i]._ty, 2).x != -1) { if (towner[i]._ttype == -1) continue; pcursmonst = i; - if (interact) { - TalkToTowner(myplr, i); - } break; } } } -bool checkMonstersNearby(bool attack) +void CheckMonstersNearby() { - int closest = 0; // monster ID who is closest coords objDistLast = { 99, 99 }; // previous obj distance // The first MAX_PLRS monsters are reserved for players' golems. for (int i = MAX_PLRS; i < MAXMONSTERS; i++) { const auto &monst = monster[i]; const int mx = monst._mx; const int my = monst._my; - if (dMonster[mx][my] == 0 || - (monst._mFlags & MFLAG_HIDDEN) || // hidden - monst._mhitpoints <= 0 || // dead - !((dFlags[mx][my] & BFLAG_LIT) || plr[myplr]._pInfraFlag)) // invisible + if (dMonster[mx][my] == 0 || (monst._mFlags & MFLAG_HIDDEN) || // hidden + monst._mhitpoints <= 0 || // dead + !((dFlags[mx][my] & BFLAG_LIT) || plr[myplr]._pInfraFlag)) // not visable continue; const char mSelFlag = monst.MData->mSelFlag; if (mSelFlag & 1 || mSelFlag & 2 || mSelFlag & 3 || mSelFlag & 4) { // is monster selectable - coords objDist = checkNearbyObjs(mx, my, 6); + coords objDist = CheckNearbyObjs(mx, my, 6); if (objDist.x > -1 && objDist.x <= objDistLast.x && objDist.y <= objDistLast.y) { - closest = i; + pcursmonst = i; objDistLast = objDist; } } } - if (closest < MAX_PLRS) { // didn't find a monster - pcursmonst = -1; - return false; - } - pcursmonst = closest; - //sprintf(tempstr, "NEARBY MONSTER WITH HP:%i", monster[closest]._mhitpoints); - //NetSendCmdString(1 << myplr, tempstr); - if (attack) { - static DWORD attacktick = 0; - DWORD ticks = GetTickCount(); - if (ticks - attacktick > 100) { // prevent accidental double attacks - attacktick = ticks; - LeftMouseCmd(false); - } - } - return true; } -// hide the cursor when we start walking via keyboard/controller -void HideCursor() +void Interact() { - if (pcurs >= CURSOR_FIRSTITEM) // drop item to allow us to pick up other items - DropItemBeforeTrig(); - SetCursorPos(SCREEN_WIDTH / 2, PANEL_TOP / 2); - if (pcurs == CURSOR_REPAIR || pcurs == CURSOR_RECHARGE) - SetCursor_(CURSOR_HAND); - sgbControllerActive = true; + if (leveltype == DTYPE_TOWN && pcursmonst != -1) { + NetSendCmdLocParam1(true, CMD_TALKXY, towner[pcursmonst]._tx, towner[pcursmonst]._ty, pcursmonst); + } else if (pcursmonst != -1) { + if (plr[myplr]._pwtype != WT_RANGED || CanTalkToMonst(pcursmonst)) { + NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst); + } else { + NetSendCmdParam1(true, CMD_RATTACKID, pcursmonst); + } + } else if (pcursplr != -1 && !FriendlyMode) { + NetSendCmdParam1(true, plr[myplr]._pwtype == WT_RANGED ? CMD_RATTACKPID : CMD_ATTACKPID, pcursplr); + } } -void attrIncBtnSnap(int key) +void AttrIncBtnSnap(MoveDirectionY dir) { - if (invflag || spselflag || !chrflag) + if (dir == MoveDirectionY::NONE) return; - if (chrbtnactive && !plr[myplr]._pStatPts) + if (chrbtnactive && plr[myplr]._pStatPts <= 0) return; DWORD ticks = GetTickCount(); - if (ticks - invmove < 80) { + if (ticks - invmove < repeatRate) { return; } invmove = ticks; @@ -176,11 +137,10 @@ void attrIncBtnSnap(int key) } } - // set future location up or down - if (key == DVL_VK_UP) { + if (dir == MoveDirectionY::UP) { if (slot > 0) --slot; - } else if (key == DVL_VK_DOWN) { + } else if (dir == MoveDirectionY::DOWN) { if (slot < 3) ++slot; } @@ -189,20 +149,15 @@ void attrIncBtnSnap(int key) int x = ChrBtnsRect[slot].x + (ChrBtnsRect[slot].w / 2); int y = ChrBtnsRect[slot].y + (ChrBtnsRect[slot].h / 2); SetCursorPos(x, y); - MouseX = x; - MouseY = y; } // move the cursor around in our inventory // if mouse coords are at SLOTXY_CHEST_LAST, consider this center of equipment // small inventory squares are 29x29 (roughly) -void invMove(int key) +void InvMove(MoveDirection dir) { - if (!invflag) - return; - DWORD ticks = GetTickCount(); - if (ticks - invmove < 80) { + if (ticks - invmove < repeatRate) { return; } invmove = ticks; @@ -223,7 +178,7 @@ void invMove(int key) slot = SLOTXY_BELT_LAST; // when item is on cursor, this is the real cursor XY - if (key == WALK_W) { + if (dir.x == MoveDirectionX::LEFT) { if (slot >= SLOTXY_HAND_RIGHT_FIRST && slot <= SLOTXY_HAND_RIGHT_LAST) { x = InvRect[SLOTXY_CHEST_FIRST].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_CHEST_FIRST].Y - (INV_SLOT_SIZE_PX / 2); @@ -251,7 +206,7 @@ void invMove(int key) y = InvRect[slot].Y - (INV_SLOT_SIZE_PX / 2); } } - } else if (key == WALK_E) { + } else if (dir.x == MoveDirectionX::RIGHT) { if (slot == SLOTXY_RING_LEFT) { x = InvRect[SLOTXY_RING_RIGHT].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_RING_RIGHT].Y - (INV_SLOT_SIZE_PX / 2); @@ -277,7 +232,8 @@ void invMove(int key) y = InvRect[slot].Y - (INV_SLOT_SIZE_PX / 2); } } - } else if (key == WALK_N) { + } + if (dir.y == MoveDirectionY::UP) { if (slot > 24 && slot <= 27) { // first 3 general slots x = InvRect[SLOTXY_RING_LEFT].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_RING_LEFT].Y - (INV_SLOT_SIZE_PX / 2); @@ -311,7 +267,7 @@ void invMove(int key) x = InvRect[slot].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[slot].Y - (INV_SLOT_SIZE_PX / 2); } - } else if (key == WALK_S) { + } else if (dir.y == MoveDirectionY::DOWN) { if (slot >= SLOTXY_HEAD_FIRST && slot <= SLOTXY_HEAD_LAST) { x = InvRect[SLOTXY_CHEST_FIRST].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_CHEST_FIRST].Y - (INV_SLOT_SIZE_PX / 2); @@ -340,14 +296,17 @@ void invMove(int key) } } - if (pcurs > 1) { // [3] Keep item in the same slot, don't jump it up - if (x != MouseX) // without this, the cursor keeps moving -10 - { - SetCursorPos(x - 10, y - 10); + if (x == MouseX && y == MouseY) { + return; // Avoid wobeling when scalled + } + + if (pcurs > 1) { // [3] Keep item in the same slot, don't jump it up + if (x != MouseX) { // without this, the cursor keeps moving -10 + x -= 10; + y -= 10; } - } else { - SetCursorPos(x, y); } + SetCursorPos(x, y); } // check if hot spell at X Y exists @@ -361,18 +320,13 @@ bool HSExists(int x, int y) return false; } -void hotSpellMove(int key) +void HotSpellMove(MoveDirection dir) { - if (!spselflag) - return; int x = 0; int y = 0; - if (!sgbControllerActive) - HideCursor(); - DWORD ticks = GetTickCount(); - if (ticks - invmove < 80) { + if (ticks - invmove < repeatRate) { return; } invmove = ticks; @@ -392,7 +346,7 @@ void hotSpellMove(int key) } } - if (key == DVL_VK_UP) { + if (dir.y == MoveDirectionY::UP) { if (speedspellscoords[spbslot].y == 307 && hsr[1] > 0) { // we're in row 1, check if row 2 has spells if (HSExists(MouseX, 251)) { x = MouseX; @@ -404,7 +358,7 @@ void hotSpellMove(int key) y = 195; } } - } else if (key == DVL_VK_DOWN) { + } else if (dir.y == MoveDirectionY::DOWN) { if (speedspellscoords[spbslot].y == 251) { // we're in row 2 if (HSExists(MouseX, 307)) { x = MouseX; @@ -416,13 +370,14 @@ void hotSpellMove(int key) y = 251; } } - } else if (key == DVL_VK_LEFT) { + } + if (dir.x == MoveDirectionX::LEFT) { if (spbslot >= speedspellcount - 1) return; spbslot++; x = speedspellscoords[spbslot].x; y = speedspellscoords[spbslot].y; - } else if (key == DVL_VK_RIGHT) { + } else if (dir.x == MoveDirectionX::RIGHT) { if (spbslot <= 0) return; spbslot--; @@ -432,85 +387,54 @@ void hotSpellMove(int key) if (x > 0 && y > 0) { SetCursorPos(x, y); - MouseX = x; - MouseY = y; } } -void walkInDir(MoveDirection dir) -{ - if (dir.x == MoveDirectionX::NONE && dir.y == MoveDirectionY::NONE) - return; - DWORD ticks = GetTickCount(); - if (ticks - invmove < 370) { - return; - } - invmove = ticks; - ClrPlrPath(myplr); // clear nodes - plr[myplr].destAction = ACTION_NONE; // stop attacking, etc. - HideCursor(); - static const _walk_path kMoveToWalkDir[3][3] = { - // NONE UP DOWN - { WALK_NONE, WALK_N, WALK_S }, // NONE - { WALK_W, WALK_NW, WALK_SW }, // LEFT - { WALK_E, WALK_NE, WALK_SE }, // RIGHT - }; - plr[myplr].walkpath[0] = kMoveToWalkDir[static_cast(dir.x)][static_cast(dir.y)]; -} +static const _walk_path kMoveToWalkDir[3][3] = { + // NONE UP DOWN + { WALK_NONE, WALK_N, WALK_S }, // NONE + { WALK_W, WALK_NW, WALK_SW }, // LEFT + { WALK_E, WALK_NE, WALK_SE }, // RIGHT +}; +static const direction kFaceDir[3][3] = { + // NONE UP DOWN + { DIR_OMNI, DIR_N, DIR_S }, // NONE + { DIR_W, DIR_NW, DIR_SW }, // LEFT + { DIR_E, DIR_NE, DIR_SE }, // RIGHT +}; -void menuMoveX(MoveDirectionX dir) +void WalkInDir(MoveDirection dir) { - if (dir == MoveDirectionX::NONE) + if (dir.x == MoveDirectionX::NONE && dir.y == MoveDirectionY::NONE) { + if (sgbControllerActive && plr[myplr].destAction == ACTION_NONE) + ClrPlrPath(myplr); return; - invMove(dir == MoveDirectionX::LEFT ? WALK_W : WALK_E); - hotSpellMove(dir == MoveDirectionX::LEFT ? DVL_VK_LEFT : DVL_VK_RIGHT); -} + } -void menuMoveY(MoveDirectionY dir) -{ - if (dir == MoveDirectionY::NONE) - return; - invMove(dir == MoveDirectionY::UP ? WALK_N : WALK_S); - const auto key = dir == MoveDirectionY::UP ? DVL_VK_UP : DVL_VK_DOWN; - hotSpellMove(key); - attrIncBtnSnap(key); + ClrPlrPath(myplr); + plr[myplr].walkpath[0] = kMoveToWalkDir[static_cast(dir.x)][static_cast(dir.y)]; + plr[myplr].destAction = ACTION_NONE; // stop attacking, etc. + plr[myplr]._pdir = kFaceDir[static_cast(dir.x)][static_cast(dir.y)]; } -void movement() +void Movement() { if (InGameMenu()) return; MoveDirection move_dir = GetMoveDirection(); - if (InControlledMenu()) { - menuMoveX(move_dir.x); - menuMoveY(move_dir.y); - } else { - walkInDir(move_dir); + if (move_dir.x != MoveDirectionX::NONE || move_dir.y != MoveDirectionY::NONE) { + sgbControllerActive = true; } - if (GetAsyncKeyState(DVL_MK_LBUTTON) & 0x8000) { - if (sgbControllerActive) { // show cursor first, before clicking - sgbControllerActive = false; - } else if (spselflag) { - SetSpell(); - } else { - LeftMouseCmd(false); - } - } -} - -// Move map or mouse no more than 60 times per second. -// This is called from both the game loop and the event loop for responsiveness. -void HandleRightStickMotionAt60Fps() -{ - static std::int64_t currentTime = 0; - static std::int64_t lastTime = 0; - currentTime = SDL_GetTicks(); - - if (currentTime - lastTime > 15) { - HandleRightStickMotion(); - lastTime = currentTime; + if (invflag) { + InvMove(move_dir); + } else if (chrflag && plr[myplr]._pStatPts > 0) { + AttrIncBtnSnap(move_dir.y); + } else if (spselflag) { + HotSpellMove(move_dir); + } else { + WalkInDir(move_dir); } } @@ -558,9 +482,7 @@ void HandleRightStickMotion() AutomapLeft(); acc.finish(); } else { // move cursor - if (sgbControllerActive) { - sgbControllerActive = false; - } + sgbControllerActive = false; static RightStickAccumulator acc = { /*slowdown=*/(1 << 13) + (1 << 12), 0, 0 }; int x = MouseX; int y = MouseY; @@ -576,32 +498,41 @@ void HandleRightStickMotion() void plrctrls_after_check_curs_move() { - HandleRightStickMotionAt60Fps(); + HandleRightStickMotion(); // check for monsters first, then items, then towners. - if (sgbControllerActive) { // cursor should be missing - if (!checkMonstersNearby(false)) { - pcursmonst = -1; - checkItemsNearby(false); - checkTownersNearby(false); - } else { - pcursitem = -1; + if (sgbControllerActive) { + // Clear focuse set by cursor + if (!invflag) { + *infostr = '\0'; + ClearPanel(); } + pcursplr = -1; + pcursmonst = -1; + pcursitem = -1; + pcursobj = -1; + + // TODO target players if !FriendlyMode + if (leveltype != DTYPE_TOWN) + CheckMonstersNearby(); + else + CheckTownersNearby(); + CheckItemsNearby(); } } void plrctrls_after_game_logic() { - movement(); + Movement(); } -void useBeltPotion(bool mana) +void UseBeltItem(int type) { for (int i = 0; i < MAXBELTITEMS; i++) { const auto id = AllItemsList[plr[myplr].SpdList[i].IDidx].iMiscId; const auto spellId = AllItemsList[plr[myplr].SpdList[i].IDidx].iSpell; - if ((!mana && (id == IMISC_HEAL || id == IMISC_FULLHEAL || (id == IMISC_SCROLL && spellId == SPL_HEAL))) - || (mana && (id == IMISC_MANA || id == IMISC_FULLMANA)) + if ((type == BLT_HEALING && (id == IMISC_HEAL || id == IMISC_FULLHEAL || (id == IMISC_SCROLL && spellId == SPL_HEAL))) + || (type == BLT_MANA && (id == IMISC_MANA || id == IMISC_FULLMANA)) || id == IMISC_REJUV || id == IMISC_FULLREJUV) { if (plr[myplr].SpdList[i]._itype > -1) { UseInvItem(myplr, INVITEM_BELT_FIRST + i); @@ -611,69 +542,52 @@ void useBeltPotion(bool mana) } } -void performPrimaryAction() +void PerformPrimaryAction() { - const DWORD ticks = GetTickCount(); if (invflag) { // inventory is open - static DWORD clickinvtimer; - if (ticks - clickinvtimer >= 300) { - clickinvtimer = ticks; - if (pcurs == CURSOR_IDENTIFY) - CheckIdentify(myplr, pcursinvitem); - else if (pcurs == CURSOR_REPAIR) - DoRepair(myplr, pcursinvitem); - else if (pcurs == CURSOR_RECHARGE) - DoRecharge(myplr, pcursinvitem); - else - CheckInvItem(); - } - } else if (spselflag) { + if (pcurs == CURSOR_IDENTIFY) + CheckIdentify(myplr, pcursinvitem); + else if (pcurs == CURSOR_REPAIR) + DoRepair(myplr, pcursinvitem); + else if (pcurs == CURSOR_RECHARGE) + DoRecharge(myplr, pcursinvitem); + else + CheckInvItem(); + return; + } + + if (spselflag) { SetSpell(); - } else if (chrflag) { - static DWORD statuptimer; - if (ticks - statuptimer >= 400) { - statuptimer = ticks; - if (!chrbtnactive && plr[myplr]._pStatPts) { - CheckChrBtns(); - for (int i = 0; i < 4; i++) { - if (MouseX >= ChrBtnsRect[i].x - && MouseX <= ChrBtnsRect[i].x + ChrBtnsRect[i].w - && MouseY >= ChrBtnsRect[i].y - && MouseY <= ChrBtnsRect[i].h + ChrBtnsRect[i].y) { - chrbtn[i] = 1; - chrbtnactive = true; - ReleaseChrBtns(); - } - } - } - if (plr[myplr]._pStatPts == 0) - HideCursor(); - } - } else { - static DWORD talkwait; - if (stextflag) - talkwait = GetTickCount(); // Wait before we re-initiate talking - HideCursor(); - DWORD talktick = GetTickCount(); - if (!checkMonstersNearby(true)) { - if (talktick - talkwait > 600) { // prevent re-entering talk after finished - talkwait = talktick; - checkTownersNearby(true); + return; + } + + if (chrflag && !chrbtnactive && plr[myplr]._pStatPts > 0) { + CheckChrBtns(); + for (int i = 0; i < 4; i++) { + if (MouseX >= ChrBtnsRect[i].x + && MouseX <= ChrBtnsRect[i].x + ChrBtnsRect[i].w + && MouseY >= ChrBtnsRect[i].y + && MouseY <= ChrBtnsRect[i].h + ChrBtnsRect[i].y) { + chrbtn[i] = 1; + chrbtnactive = true; + ReleaseChrBtns(); } } + return; } + + Interact(); } -void performSecondaryAction() +void PerformSecondaryAction() { if (invflag) return; - static DWORD opentimer = 0; - HideCursor(); - const DWORD ticks = GetTickCount(); - if (ticks - opentimer > 500) { - opentimer = ticks; - checkItemsNearby(true); + + if (pcursitem != -1 && pcurs == CURSOR_HAND) { + NetSendCmdLocParam1(pcurs, CMD_GOTOAGETITEM, item[pcursitem]._ix, item[pcursitem]._iy, pcursitem); + } else if (pcursobj != -1) { + NetSendCmdLocParam1(true, pcurs == CURSOR_DISARM ? CMD_DISARMXY : CMD_OPOBJXY, object[pcursobj]._ox, object[pcursobj]._oy, pcursobj); } } diff --git a/SourceX/controls/plrctrls.h b/SourceX/controls/plrctrls.h index 28a02ddd1..ad58b8eae 100644 --- a/SourceX/controls/plrctrls.h +++ b/SourceX/controls/plrctrls.h @@ -5,6 +5,11 @@ namespace dvl { +typedef enum belt_item_type { + BLT_HEALING, + BLT_MANA, +}; + // Run after every game logic iteration. // Handles player and menu movement. void plrctrls_after_game_logic(); @@ -19,13 +24,13 @@ void HandleRightStickMotion(); // Whether we're in a dialog menu that the game handles natively with keyboard controls. bool InGameMenu(); -void useBeltPotion(bool mana); +void UseBeltItem(int type); // Talk to towners, click on inv items, attack, etc. -void performPrimaryAction(); +void PerformPrimaryAction(); // Open chests, doors, pickup items. -void performSecondaryAction(); +void PerformSecondaryAction(); typedef struct coords { int x; diff --git a/SourceX/miniwin/misc_dx.cpp b/SourceX/miniwin/misc_dx.cpp deleted file mode 100644 index 334d5cc3c..000000000 --- a/SourceX/miniwin/misc_dx.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "devilution.h" -#include "miniwin/ddraw.h" -#include "stubs.h" -#include - -namespace dvl { - -WINBOOL SetCursorPos(int X, int Y) -{ - assert(window); - -#ifndef USE_SDL1 - if (renderer) { - SDL_Rect view; - SDL_RenderGetViewport(renderer, &view); - X += view.x; - Y += view.y; - - float scaleX; - SDL_RenderGetScale(renderer, &scaleX, NULL); - X *= scaleX; - Y *= scaleX; - } -#endif - - SDL_WarpMouseInWindow(window, X, Y); - MouseX = X; - MouseY = Y; - return true; -} - -} // namespace dvl diff --git a/SourceX/miniwin/misc_msg.cpp b/SourceX/miniwin/misc_msg.cpp index b3438a253..109e834e4 100644 --- a/SourceX/miniwin/misc_msg.cpp +++ b/SourceX/miniwin/misc_msg.cpp @@ -3,6 +3,7 @@ #include #include "devilution.h" +#include "miniwin/ddraw.h" #include "stubs.h" #include "controls/controller_motion.h" #include "controls/game_controls.h" @@ -17,6 +18,28 @@ namespace dvl { static std::deque message_queue; +bool mouseWarping = false; + +void SetCursorPos(int X, int Y) +{ +#ifndef USE_SDL1 + if (renderer) { + SDL_Rect view; + SDL_RenderGetViewport(renderer, &view); + X += view.x; + Y += view.y; + + float scaleX; + SDL_RenderGetScale(renderer, &scaleX, NULL); + X *= scaleX; + Y *= scaleX; + } +#endif + + mouseWarping = true; + SDL_WarpMouseInWindow(nullptr, X, Y); +} + static int translate_sdl_key(SDL_Keysym key) { // ref: https://wiki.libsdl.org/SDL_Keycode @@ -229,35 +252,19 @@ void SetMouseLMBMessage(const SDL_Event &event, LPMSG lpMsg) lpMsg->wParam = keystate_for_mouse(lpMsg->message == DVL_WM_LBUTTONUP ? 0 : DVL_MK_LBUTTON); } -void ShowCursor() -{ - if (sgbControllerActive) { - sgbControllerActive = false; - } -} - -void HideCursorIfNotNeeded() -{ - if (!chrflag && !invflag) { - sgbControllerActive = true; - } -} - // Moves the mouse to the first inventory slot. -bool FocusOnInventory() +void FocusOnInventory() { if (!invflag) - return false; - ShowCursor(); + return; SetCursorPos(InvRect[25].X + (INV_SLOT_SIZE_PX / 2), InvRect[25].Y - (INV_SLOT_SIZE_PX / 2)); - return true; } // Moves the mouse to the first attribute "+" button. -bool FocusOnCharInfo() +void FocusOnCharInfo() { if (!chrflag || plr[myplr]._pStatPts == 0) - return false; + return; // Find the first incrementable stat. int pc = plr[myplr]._pClass; @@ -286,11 +293,9 @@ bool FocusOnCharInfo() stat = i; } if (stat == -1) - return false; - ShowCursor(); + return; const auto &rect = ChrBtnsRect[stat]; SetCursorPos(rect.x + (rect.w / 2), rect.y + (rect.h / 2)); - return true; } void StoreSpellCoords() @@ -343,6 +348,17 @@ void StoreSpellCoords() } // namespace +/** + * @brief Clean the inventory related cursor states. + */ +void BlurInventory() +{ + if (pcurs >= CURSOR_FIRSTITEM) // drop item to allow us to pick up other items + DropItemBeforeTrig(); + if (pcurs == CURSOR_REPAIR || pcurs == CURSOR_RECHARGE) + SetCursor_(CURSOR_HAND); +} + WINBOOL PeekMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg) { if (wMsgFilterMin != 0) @@ -390,26 +406,29 @@ WINBOOL PeekMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilter if (ProcessControllerMotion(e)) { ScaleJoysticks(); - HandleRightStickMotion(); return true; } GameAction action; if (GetGameAction(e, &action)) { + if (action.type != GameActionType::NONE) { + sgbControllerActive = true; + } + switch (action.type) { case GameActionType::NONE: break; case GameActionType::USE_HEALTH_POTION: - useBeltPotion(/*mana=*/false); + UseBeltItem(BLT_HEALING); break; case GameActionType::USE_MANA_POTION: - useBeltPotion(/*mana=*/true); + UseBeltItem(BLT_MANA); break; case GameActionType::PRIMARY_ACTION: - performPrimaryAction(); + PerformPrimaryAction(); break; case GameActionType::SECONDARY_ACTION: - performSecondaryAction(); + PerformSecondaryAction(); break; case GameActionType::CAST_SPELL: if (pcurs >= CURSOR_FIRSTITEM && invflag) @@ -422,12 +441,10 @@ WINBOOL PeekMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilter PressEscKey(); break; case GameActionType::TOGGLE_QUICK_SPELL_MENU: - ShowCursor(); lpMsg->message = DVL_WM_KEYDOWN; lpMsg->wParam = 'S'; - HideCursorIfNotNeeded(); StoreSpellCoords(); - return true; + break; case GameActionType::TOGGLE_CHARACTER_INFO: questlog = false; chrflag = !chrflag; @@ -435,7 +452,6 @@ WINBOOL PeekMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilter FocusOnCharInfo(); else FocusOnInventory(); - HideCursorIfNotNeeded(); break; case GameActionType::TOGGLE_INVENTORY: if (pcurs >= CURSOR_FIRSTITEM && invflag) @@ -447,14 +463,13 @@ WINBOOL PeekMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilter FocusOnInventory(); else FocusOnCharInfo(); - HideCursorIfNotNeeded(); break; case GameActionType::SEND_KEY: lpMsg->message = action.send_key.up ? DVL_WM_KEYUP : DVL_WM_KEYDOWN; lpMsg->wParam = action.send_key.vk_code; return true; case GameActionType::SEND_MOUSE_CLICK: - ShowCursor(); + sgbControllerActive = false; switch (action.send_mouse_click.button) { case GameActionSendMouseClick::LEFT: lpMsg->message = action.send_mouse_click.up ? DVL_WM_LBUTTONUP : DVL_WM_LBUTTONDOWN; @@ -464,9 +479,14 @@ WINBOOL PeekMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilter break; } lpMsg->lParam = (MouseY << 16) | (MouseX & 0xFFFF); - return true; + break; } return true; + } else if (e.type < SDL_JOYAXISMOTION || e.type >= 0x700) { + if (!mouseWarping || e.type != SDL_MOUSEMOTION) + sgbControllerActive = false; + if (mouseWarping && e.type == SDL_MOUSEMOTION) + mouseWarping = false; } switch (e.type) {