You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
686 lines
21 KiB
686 lines
21 KiB
#include "controls/plrctrls.h" |
|
|
|
#include <cstdint> |
|
|
|
#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<std::size_t>(dir.x)][static_cast<std::size_t>(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_game_logic() |
|
{ |
|
// 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; |
|
} |
|
} |
|
movement(); |
|
HandleRightStickMotionAt60Fps(); |
|
} |
|
|
|
void plrctrls_event_loop() |
|
{ |
|
HandleRightStickMotionAt60Fps(); |
|
} |
|
|
|
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
|
|
|