#include "controls/plrctrls.h" #include #include "controls/controller_motion.h" #include "controls/game_controls.h" // Based on the Nintendo Switch port by @lantus, @erfg12, @rsn8887. namespace dvl { bool sgbControllerActive = false; coords speedspellscoords[50]; int speedspellcount = 0; // Native game menu, controlled by simulating a keyboard. bool InGameMenu() { return stextflag > 0 || questlog || helpflag || talkflag || qtextflag || sgpCurrentMenu; } namespace { DWORD invmove = 0; 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) { int diff_x = abs(plr[myplr]._px - x); int diff_y = abs(plr[myplr]._py - y); if (diff_x <= diff && diff_y <= diff) { coords cm = { diff_x, diff_y }; //sprintf(tempstr, "N-DIFF X:%i Y:%i", diff_x, diff_y); //NetSendCmdString(1 << myplr, tempstr); return cm; } return { -1, -1 }; } void checkItemsNearby(bool interact) { 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 (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); } 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 pcursobj = i; if (interact) { LeftMouseCmd(false); } return; } } if (sgbControllerActive) pcursobj = -1; } void checkTownersNearby(bool interact) { 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 (towner[i]._ttype == -1) continue; pcursmonst = i; if (interact) { TalkToTowner(myplr, i); } break; } } } bool checkMonstersNearby(bool attack) { 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++) { int d_monster = dMonster[monster[i]._mx][monster[i]._my]; if (monster[i]._mFlags & MFLAG_HIDDEN || monster[i]._mhitpoints <= 0) // monster is hiding or dead, skip continue; if (d_monster && dFlags[monster[i]._mx][monster[i]._my] & BFLAG_LIT) { // is monster visible if (monster[i].MData->mSelFlag & 1 || monster[i].MData->mSelFlag & 2 || monster[i].MData->mSelFlag & 3 || monster[i].MData->mSelFlag & 4) { // is monster selectable coords objDist = checkNearbyObjs(monster[i]._mx, monster[i]._my, 6); if (objDist.x > -1 && objDist.x <= objDistLast.x && objDist.y <= objDistLast.y) { closest = i; objDistLast = objDist; } } } } if (closest > 0) { // did we find a monster pcursmonst = closest; //sprintf(tempstr, "NEARBY MONSTER WITH HP:%i", monster[closest]._mhitpoints); //NetSendCmdString(1 << myplr, tempstr); } else if (closest > 0) { // found monster, but we don't want to attack it return true; } else { pcursmonst = -1; return false; } if (attack) { static DWORD attacktick = 0; DWORD ticks = GetTickCount(); if (ticks - attacktick > 100) { // prevent accidental double attacks attacktick = ticks; LeftMouseCmd(false); } return true; } else { return true; } pcursmonst = -1; return false; } // hide the cursor when we start walking via keyboard/controller void HideCursor() { if (pcurs >= CURSOR_FIRSTITEM) // if we don't drop the item on cursor, it will be destroyed DropItemBeforeTrig(); SetCursorPos(320, 180); MouseX = 320; MouseY = 180; sgbControllerActive = true; } void attrIncBtnSnap(int key) { if (invflag || spselflag || !chrflag) return; if (chrbtnactive && !plr[myplr]._pStatPts) return; DWORD ticks = GetTickCount(); if (ticks - invmove < 80) { return; } invmove = ticks; // first, find our cursor location int slot = 0; 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) { slot = i; break; } } // set future location up or down if (key == DVL_VK_UP) { if (slot > 0) --slot; } else if (key == DVL_VK_DOWN) { if (slot < 3) ++slot; } // move cursor to our new location 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) { if (!invflag) return; DWORD ticks = GetTickCount(); if (ticks - invmove < 80) { return; } invmove = ticks; int x = MouseX; int y = MouseY; // check which inventory rectangle the mouse is in, if any for (int r = 0; (DWORD)r < NUM_XY_SLOTS; r++) { if (x >= InvRect[r].X && x < InvRect[r].X + (INV_SLOT_SIZE_PX + 1) && y >= InvRect[r].Y - (INV_SLOT_SIZE_PX + 1) && y < InvRect[r].Y) { slot = r; break; } } if (slot < 0) slot = 0; if (slot > SLOTXY_BELT_LAST) slot = SLOTXY_BELT_LAST; // when item is on cursor, this is the real cursor XY if (key == WALK_W) { 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); } else if (slot >= SLOTXY_CHEST_FIRST && slot <= SLOTXY_CHEST_LAST) { x = InvRect[SLOTXY_HAND_LEFT_FIRST + 2].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_HAND_LEFT_FIRST + 2].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot == SLOTXY_AMULET) { x = InvRect[SLOTXY_HEAD_FIRST].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_HEAD_FIRST].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot == SLOTXY_RING_RIGHT) { x = InvRect[SLOTXY_RING_LEFT].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_RING_LEFT].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot == SLOTXY_BELT_FIRST) { // do nothing } else if (slot == SLOTXY_RING_LEFT) { // left ring // do nothing } else if (slot >= SLOTXY_HAND_LEFT_FIRST && slot <= SLOTXY_HAND_LEFT_LAST) { // left hand // do nothing } else if (slot >= SLOTXY_HEAD_FIRST && slot <= SLOTXY_HEAD_LAST) { // head // do nothing } else if (slot > SLOTXY_INV_FIRST) { // general inventory if (slot != SLOTXY_INV_FIRST && slot != 35 && slot != 45 && slot != 55) { // left bounds slot -= 1; x = InvRect[slot].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[slot].Y - (INV_SLOT_SIZE_PX / 2); } } } else if (key == WALK_E) { 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); } else if (slot >= SLOTXY_HAND_LEFT_FIRST && slot <= SLOTXY_HAND_LEFT_LAST) { x = InvRect[SLOTXY_CHEST_FIRST].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_CHEST_FIRST].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot >= SLOTXY_CHEST_FIRST && slot <= SLOTXY_CHEST_LAST) { x = InvRect[SLOTXY_HAND_RIGHT_FIRST + 2].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_HAND_RIGHT_FIRST + 2].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot >= SLOTXY_HEAD_FIRST && slot <= SLOTXY_HEAD_LAST) { // head to amulet x = InvRect[SLOTXY_AMULET].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_AMULET].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot >= SLOTXY_HAND_RIGHT_FIRST && slot <= SLOTXY_HAND_RIGHT_LAST) { // right hand // do nothing } else if (slot == SLOTXY_AMULET) { // do nothing } else if (slot == SLOTXY_RING_RIGHT) { // do nothing } else if (slot < SLOTXY_BELT_LAST && slot >= SLOTXY_INV_FIRST) { // general inventory if (slot != 34 && slot != 44 && slot != 54 && slot != SLOTXY_INV_LAST) { // right bounds slot += 1; x = InvRect[slot].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[slot].Y - (INV_SLOT_SIZE_PX / 2); } } } else if (key == WALK_N) { 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); } else if (slot >= 28 && slot <= 32) { // middle 4 general slots x = InvRect[SLOTXY_CHEST_FIRST].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_CHEST_FIRST].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot >= 33 && slot < 35) { // last 3 general slots x = InvRect[SLOTXY_RING_RIGHT].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_RING_RIGHT].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot >= SLOTXY_CHEST_FIRST && slot <= SLOTXY_CHEST_LAST) { // chest to head x = InvRect[SLOTXY_HEAD_FIRST].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_HEAD_FIRST].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot == SLOTXY_RING_LEFT) { // left ring to left hand x = InvRect[SLOTXY_HAND_LEFT_FIRST + 2].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_HAND_LEFT_FIRST + 2].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot == SLOTXY_RING_RIGHT) { // right ring to right hand x = InvRect[SLOTXY_HAND_RIGHT_FIRST + 2].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_HAND_RIGHT_FIRST + 2].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot >= SLOTXY_HAND_RIGHT_FIRST && slot <= SLOTXY_HAND_RIGHT_LAST) { // right hand to amulet x = InvRect[SLOTXY_AMULET].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_AMULET].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot >= SLOTXY_HEAD_FIRST && slot <= SLOTXY_HEAD_LAST) { // do nothing } else if (slot >= SLOTXY_HAND_LEFT_FIRST && slot <= SLOTXY_HAND_LEFT_LAST) { // left hand to head x = InvRect[SLOTXY_HEAD_FIRST].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_HEAD_FIRST].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot == SLOTXY_AMULET) { // do nothing } else if (slot >= (SLOTXY_INV_FIRST + 10)) { // general inventory slot -= 10; x = InvRect[slot].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[slot].Y - (INV_SLOT_SIZE_PX / 2); } } else if (key == WALK_S) { 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); } else if (slot >= SLOTXY_CHEST_FIRST && slot <= SLOTXY_CHEST_LAST) { x = InvRect[30].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[30].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot >= SLOTXY_HAND_LEFT_FIRST && slot <= SLOTXY_HAND_LEFT_LAST) { x = InvRect[SLOTXY_RING_LEFT].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_RING_LEFT].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot == SLOTXY_RING_LEFT) { x = InvRect[26].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[26].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot == SLOTXY_RING_RIGHT) { x = InvRect[34].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[34].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot == SLOTXY_AMULET) { x = InvRect[SLOTXY_HAND_RIGHT_FIRST + 2].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_HAND_RIGHT_FIRST + 2].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot >= SLOTXY_HAND_RIGHT_FIRST && slot <= SLOTXY_HAND_RIGHT_LAST) { x = InvRect[SLOTXY_RING_RIGHT].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[SLOTXY_RING_RIGHT].Y - (INV_SLOT_SIZE_PX / 2); } else if (slot < (SLOTXY_BELT_LAST - 10)) { // general inventory slot += 10; x = InvRect[slot].X + (INV_SLOT_SIZE_PX / 2); y = InvRect[slot].Y - (INV_SLOT_SIZE_PX / 2); } } 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); } } else { SetCursorPos(x, y); } } // check if hot spell at X Y exists bool HSExists(int x, int y) { for (int r = 0; r < speedspellcount; r++) { // speedbook cells are 56x56 if (MouseX >= speedspellscoords[r].x - 28 && MouseX < speedspellscoords[r].x + (28) && MouseY >= speedspellscoords[r].y - (28) && MouseY < speedspellscoords[r].y + 28) { return true; } } return false; } void hotSpellMove(int key) { if (!spselflag) return; int x = 0; int y = 0; if (!sgbControllerActive) HideCursor(); DWORD ticks = GetTickCount(); if (ticks - invmove < 80) { return; } invmove = ticks; for (int r = 0; r < speedspellcount; r++) { // speedbook cells are 56x56 // our 3 rows by y axis if (speedspellscoords[r].y == 307) hsr[0]++; if (speedspellscoords[r].y == 251) hsr[1]++; if (speedspellscoords[r].y == 195) hsr[2]++; if (MouseX >= speedspellscoords[r].x - 28 && MouseX < speedspellscoords[r].x + (28) && MouseY >= speedspellscoords[r].y - (28) && MouseY < speedspellscoords[r].y + 28) { spbslot = r; //sprintf(tempstr, "IN HOT SPELL CELL NUM:%i", r); //NetSendCmdString(1 << myplr, tempstr); } } if (key == DVL_VK_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; y = 251; } } else if (speedspellscoords[spbslot].y == 251 && hsr[2] > 0) { // we're in row 2, check if row 3 has spells if (HSExists(MouseX, 195)) { x = MouseX; y = 195; } } } else if (key == DVL_VK_DOWN) { if (speedspellscoords[spbslot].y == 251) { // we're in row 2 if (HSExists(MouseX, 307)) { x = MouseX; y = 307; } } else if (speedspellscoords[spbslot].y == 195) { // we're in row 3 if (HSExists(MouseX, 251)) { x = MouseX; y = 251; } } } else if (key == DVL_VK_LEFT) { if (spbslot >= speedspellcount - 1) return; spbslot++; x = speedspellscoords[spbslot].x; y = speedspellscoords[spbslot].y; } else if (key == DVL_VK_RIGHT) { if (spbslot <= 0) return; spbslot--; x = speedspellscoords[spbslot].x; y = speedspellscoords[spbslot].y; } 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)]; } void menuMoveX(MoveDirectionX dir) { if (dir == MoveDirectionX::NONE) 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); } void movement() { if (InGameMenu()) return; MoveDirection move_dir = GetMoveDirection(); if (InControlledMenu()) { menuMoveX(move_dir.x); menuMoveY(move_dir.y); } else { walkInDir(move_dir); } 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; } } struct RightStickAccumulator { void start(int *x, int *y) { hiresDX += rightStickX * kGranularity; hiresDY += rightStickY * kGranularity; *x += hiresDX / slowdown; *y += -hiresDY / slowdown; } void finish() { // keep track of remainder for sub-pixel motion hiresDX %= slowdown; hiresDY %= slowdown; } static const int kGranularity = (1 << 15) - 1; int slowdown; // < kGranularity int hiresDX; int hiresDY; }; } // namespace void HandleRightStickMotion() { // deadzone is handled in ScaleJoystickAxes() already if (rightStickX == 0 && rightStickY == 0) return; if (automapflag) { // move map static RightStickAccumulator acc = { /*slowdown=*/(1 << 14) + (1 << 13), 0, 0 }; int dx = 0, dy = 0; acc.start(&dx, &dy); if (dy > 1) AutomapUp(); else if (dy < -1) AutomapDown(); else if (dx < -1) AutomapRight(); else if (dx > 1) AutomapLeft(); acc.finish(); } else { // move cursor if (sgbControllerActive) { sgbControllerActive = false; } static RightStickAccumulator acc = { /*slowdown=*/(1 << 13) + (1 << 12), 0, 0 }; int x = MouseX; int y = MouseY; acc.start(&x, &y); if (x < 0) x = 0; if (y < 0) y = 0; SetCursorPos(x, y); acc.finish(); } } void plrctrls_after_check_curs_move() { HandleRightStickMotionAt60Fps(); // 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; } } } void plrctrls_after_game_logic() { movement(); } void useBeltPotion(bool mana) { static DWORD menuopenslow = 0; DWORD ticks = GetTickCount(); int invNum = 0; if (ticks - menuopenslow < 300) { return; } menuopenslow = ticks; for (int i = 0; i < MAXBELTITEMS; i++) { const auto id = AllItemsList[plr[myplr].SpdList[i].IDidx].iMiscId; if ((!mana && (id == IMISC_HEAL || id == IMISC_FULLHEAL)) || (mana && (id == IMISC_MANA || id == IMISC_FULLMANA)) || id == IMISC_REJUV || id == IMISC_FULLREJUV) { if (plr[myplr].SpdList[i]._itype > -1) { invNum = i + INVITEM_BELT_FIRST; UseInvItem(myplr, invNum); break; } } } } 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 CheckInvItem(); } } else 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); } } } } void performSecondaryAction() { if (invflag) return; static DWORD opentimer = 0; HideCursor(); const DWORD ticks = GetTickCount(); if (ticks - opentimer > 500) { opentimer = ticks; checkItemsNearby(true); } } } // namespace dvl