Browse Source

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
pull/420/head
Anders Jenbo 6 years ago
parent
commit
45844b71ae
  1. 1
      CMakeLists.txt
  2. 6
      Source/cursor.cpp
  3. 6
      Source/diablo.cpp
  4. 3
      Source/inv.cpp
  5. 6
      Source/scrollrt.cpp
  6. 2
      SourceS/miniwin/misc.h
  7. 3
      SourceX/DiabloUI/diabloui.cpp
  8. 3
      SourceX/controls/controller.cpp
  9. 3
      SourceX/controls/game_controls.cpp
  10. 3
      SourceX/controls/menu_controls.cpp
  11. 376
      SourceX/controls/plrctrls.cpp
  12. 11
      SourceX/controls/plrctrls.h
  13. 32
      SourceX/miniwin/misc_dx.cpp
  14. 90
      SourceX/miniwin/misc_msg.cpp

1
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

6
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

6
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) {

3
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)

6
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;
}

2
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);

3
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

3
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:

3
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;

3
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) {

376
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<std::size_t>(dir.x)][static_cast<std::size_t>(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<std::size_t>(dir.x)][static_cast<std::size_t>(dir.y)];
plr[myplr].destAction = ACTION_NONE; // stop attacking, etc.
plr[myplr]._pdir = kFaceDir[static_cast<std::size_t>(dir.x)][static_cast<std::size_t>(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);
}
}

11
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;

32
SourceX/miniwin/misc_dx.cpp

@ -1,32 +0,0 @@
#include "devilution.h"
#include "miniwin/ddraw.h"
#include "stubs.h"
#include <SDL.h>
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

90
SourceX/miniwin/misc_msg.cpp

@ -3,6 +3,7 @@
#include <SDL.h>
#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<MSG> 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) {

Loading…
Cancel
Save