Browse Source

Fix edge cases in mouse action tracking code (#2452)

Fixes #2431

Also:
- Track spell targets
- Merge repeat walk in to the new repeat action code
- Avoid time based repeats (use action frame instead)
pull/2458/head
Anders Jenbo 5 years ago committed by GitHub
parent
commit
ab8afa7cd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      Source/controls/plrctrls.cpp
  2. 4
      Source/cursor.cpp
  3. 134
      Source/diablo.cpp
  4. 4
      Source/diablo.h
  5. 2
      Source/objects.cpp
  6. 25
      Source/player.cpp
  7. 19
      Source/player.h
  8. 108
      Source/track.cpp
  9. 3
      Source/track.h

17
Source/controls/plrctrls.cpp

@ -1059,19 +1059,6 @@ bool IsPathBlocked(Point position, Direction dir)
return !PosOkPlayer(myPlayer, leftStep) && !PosOkPlayer(myPlayer, rightStep);
}
bool CanChangeDirection(const PlayerStruct &player)
{
if (player._pmode == PM_STAND)
return true;
if (player._pmode == PM_ATTACK && player.AnimInfo.CurrentFrame > player._pAFNum)
return true;
if (player._pmode == PM_RATTACK && player.AnimInfo.CurrentFrame > player._pAFNum)
return true;
if (player._pmode == PM_SPELL && player.AnimInfo.CurrentFrame > player._pSFNum)
return true;
return false;
}
void WalkInDir(int playerId, AxisDirection dir)
{
auto &player = Players[playerId];
@ -1085,7 +1072,7 @@ void WalkInDir(int playerId, AxisDirection dir)
const Direction pdir = FaceDir[static_cast<std::size_t>(dir.x)][static_cast<std::size_t>(dir.y)];
const auto delta = player.position.future + pdir;
if (CanChangeDirection(player))
if (!player.IsWalking() && player.CanChangeAction())
player._pdir = pdir;
if (PosOkPlayer(player, delta) && IsPathBlocked(player.position.future, pdir))
@ -1481,7 +1468,7 @@ void PerformSpellAction()
}
UpdateSpellTarget();
CheckPlrSpell(false);
CheckPlrSpell();
}
void CtrlUseInvItem()

4
Source/cursor.cpp

@ -373,8 +373,8 @@ void CheckCursMove()
my = MAXDUNY - 1;
}
// While holding down left click we should retain target (but potentially lose it if it dies, goes out of view, etc)
if (sgbMouseDown == CLICK_LEFT && pcursinvitem == -1) {
// While holding the button down we should retain target (but potentially lose it if it dies, goes out of view, etc)
if (sgbMouseDown != CLICK_NONE && pcursinvitem == -1) {
if (pcursmonst != -1) {
if (Monsters[pcursmonst]._mDelFlag || Monsters[pcursmonst]._mhitpoints >> 6 <= 0
|| ((dFlags[Monsters[pcursmonst].position.tile.x][Monsters[pcursmonst].position.tile.y] & BFLAG_VISIBLE) == 0))

134
Source/diablo.cpp

@ -184,7 +184,7 @@ void StartGame(interface_mode uMsg)
InitLevelCursor();
sgnTimeoutCurs = CURSOR_NONE;
sgbMouseDown = CLICK_NONE;
track_repeat_walk(false);
LastMouseButtonAction = MouseActionType::None;
}
void FreeGame()
@ -225,13 +225,13 @@ bool ProcessInput()
#endif
CheckCursMove();
plrctrls_after_check_curs_move();
track_process();
RepeatMouseAction();
}
return true;
}
bool LeftMouseCmd(bool bShift)
void LeftMouseCmd(bool bShift)
{
bool bNear;
@ -242,94 +242,95 @@ bool LeftMouseCmd(bool bShift)
NetSendCmdLocParam1(true, invflag ? CMD_GOTOGETITEM : CMD_GOTOAGETITEM, { cursmx, cursmy }, pcursitem);
if (pcursmonst != -1)
NetSendCmdLocParam1(true, CMD_TALKXY, { cursmx, cursmy }, pcursmonst);
if (pcursitem == -1 && pcursmonst == -1 && pcursplr == -1)
return true;
if (pcursitem == -1 && pcursmonst == -1 && pcursplr == -1) {
LastMouseButtonAction = MouseActionType::Walk;
}
return;
}
auto &myPlayer = Players[MyPlayerId];
bNear = myPlayer.position.tile.WalkingDistance({ cursmx, cursmy }) < 2;
if (pcursitem != -1 && pcurs == CURSOR_HAND && !bShift) {
NetSendCmdLocParam1(true, invflag ? CMD_GOTOGETITEM : CMD_GOTOAGETITEM, { cursmx, cursmy }, pcursitem);
} else if (pcursobj != -1 && (!objectIsDisabled(pcursobj)) && (!bShift || (bNear && Objects[pcursobj]._oBreak == 1))) {
LastMouseButtonAction = MouseActionType::OperateObject;
NetSendCmdLocParam1(true, pcurs == CURSOR_DISARM ? CMD_DISARMXY : CMD_OPOBJXY, { cursmx, cursmy }, pcursobj);
} else if (myPlayer.UsesRangedWeapon()) {
if (bShift) {
LastMouseButtonAction = MouseActionType::Attack;
NetSendCmdLoc(MyPlayerId, true, CMD_RATTACKXY, { cursmx, cursmy });
} else if (pcursmonst != -1) {
if (CanTalkToMonst(Monsters[pcursmonst])) {
NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst);
} else {
LastMouseButtonAction = MouseActionType::AttackMonsterTarget;
NetSendCmdParam1(true, CMD_RATTACKID, pcursmonst);
}
} else if (pcursplr != -1 && !gbFriendlyMode) {
LastMouseButtonAction = MouseActionType::AttackPlayerTarget;
NetSendCmdParam1(true, CMD_RATTACKPID, pcursplr);
}
} else {
auto &myPlayer = Players[MyPlayerId];
bNear = myPlayer.position.tile.WalkingDistance({ cursmx, cursmy }) < 2;
if (pcursitem != -1 && pcurs == CURSOR_HAND && !bShift) {
NetSendCmdLocParam1(true, invflag ? CMD_GOTOGETITEM : CMD_GOTOAGETITEM, { cursmx, cursmy }, pcursitem);
} else if (pcursobj != -1 && (!objectIsDisabled(pcursobj)) && (!bShift || (bNear && Objects[pcursobj]._oBreak == 1))) {
LastMouseButtonAction = MouseActionType::OperateObject;
NetSendCmdLocParam1(true, pcurs == CURSOR_DISARM ? CMD_DISARMXY : CMD_OPOBJXY, { cursmx, cursmy }, pcursobj);
} else if (myPlayer.UsesRangedWeapon()) {
if (bShift) {
LastMouseButtonAction = MouseActionType::Attack;
NetSendCmdLoc(MyPlayerId, true, CMD_RATTACKXY, { cursmx, cursmy });
} else if (pcursmonst != -1) {
if (bShift) {
if (pcursmonst != -1) {
if (CanTalkToMonst(Monsters[pcursmonst])) {
NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst);
} else {
LastMouseButtonAction = MouseActionType::AttackMonsterTarget;
NetSendCmdParam1(true, CMD_RATTACKID, pcursmonst);
}
} else if (pcursplr != -1 && !gbFriendlyMode) {
LastMouseButtonAction = MouseActionType::AttackPlayerTarget;
NetSendCmdParam1(true, CMD_RATTACKPID, pcursplr);
}
} else {
if (bShift) {
if (pcursmonst != -1) {
if (CanTalkToMonst(Monsters[pcursmonst])) {
NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst);
} else {
LastMouseButtonAction = MouseActionType::Attack;
NetSendCmdLoc(MyPlayerId, true, CMD_SATTACKXY, { cursmx, cursmy });
}
} else {
LastMouseButtonAction = MouseActionType::Attack;
NetSendCmdLoc(MyPlayerId, true, CMD_SATTACKXY, { cursmx, cursmy });
}
} else if (pcursmonst != -1) {
LastMouseButtonAction = MouseActionType::AttackMonsterTarget;
NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst);
} else if (pcursplr != -1 && !gbFriendlyMode) {
LastMouseButtonAction = MouseActionType::AttackPlayerTarget;
NetSendCmdParam1(true, CMD_ATTACKPID, pcursplr);
} else {
LastMouseButtonAction = MouseActionType::Attack;
NetSendCmdLoc(MyPlayerId, true, CMD_SATTACKXY, { cursmx, cursmy });
}
} else if (pcursmonst != -1) {
LastMouseButtonAction = MouseActionType::AttackMonsterTarget;
NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst);
} else if (pcursplr != -1 && !gbFriendlyMode) {
LastMouseButtonAction = MouseActionType::AttackPlayerTarget;
NetSendCmdParam1(true, CMD_ATTACKPID, pcursplr);
}
if (!bShift && pcursitem == -1 && pcursobj == -1 && pcursmonst == -1 && pcursplr == -1)
return true;
}
return false;
if (!bShift && pcursitem == -1 && pcursobj == -1 && pcursmonst == -1 && pcursplr == -1) {
LastMouseButtonAction = MouseActionType::Walk;
}
}
bool LeftMouseDown(int wParam)
void LeftMouseDown(int wParam)
{
LastMouseButtonAction = MouseActionType::Other;
LastMouseButtonAction = MouseActionType::None;
LastMouseButtonTime = SDL_GetTicks();
if (gmenu_left_mouse(true))
return false;
return;
if (control_check_talk_btn())
return false;
return;
if (sgnTimeoutCurs != CURSOR_NONE)
return false;
return;
if (MyPlayerIsDead) {
control_check_btn_press();
return false;
return;
}
if (PauseMode == 2) {
return false;
return;
}
if (DoomFlag) {
doom_close();
return false;
return;
}
if (spselflag) {
SetSpell();
return false;
return;
}
if (stextflag != STORE_NONE) {
CheckStoreBtn();
return false;
return;
}
bool isShiftHeld = (wParam & DVL_MK_SHIFT) != 0;
@ -357,7 +358,7 @@ bool LeftMouseDown(int wParam)
if (Players[MyPlayerId]._pStatPts != 0 && !spselflag)
CheckLvlBtn();
if (!lvlbtndown)
return LeftMouseCmd(isShiftHeld);
LeftMouseCmd(isShiftHeld);
}
}
} else {
@ -367,8 +368,6 @@ bool LeftMouseDown(int wParam)
if (pcurs > CURSOR_HAND && pcurs < CURSOR_FIRSTITEM)
NewCursor(CURSOR_HAND);
}
return false;
}
void LeftMouseUp(int wParam)
@ -388,7 +387,7 @@ void LeftMouseUp(int wParam)
void RightMouseDown()
{
LastMouseButtonAction = MouseActionType::Other;
LastMouseButtonAction = MouseActionType::None;
LastMouseButtonTime = SDL_GetTicks();
if (gmenu_is_active() || sgnTimeoutCurs != CURSOR_NONE || PauseMode == 2 || Players[MyPlayerId]._pInvincible) {
@ -411,7 +410,7 @@ void RightMouseDown()
&& (pcursinvitem == -1 || !UseInvItem(MyPlayerId, pcursinvitem)))) {
if (pcurs == CURSOR_HAND) {
if (pcursinvitem == -1 || !UseInvItem(MyPlayerId, pcursinvitem))
CheckPlrSpell(true);
CheckPlrSpell();
} else if (pcurs > CURSOR_HAND && pcurs < CURSOR_FIRSTITEM) {
NewCursor(CURSOR_HAND);
}
@ -488,7 +487,7 @@ void PressKey(int vkey)
}
if (vkey == DVL_VK_ESCAPE) {
if (!PressEscKey()) {
track_repeat_walk(false);
LastMouseButtonAction = MouseActionType::None;
gamemenu_on();
}
return;
@ -734,7 +733,7 @@ void GameEventHandler(uint32_t uMsg, int32_t wParam, int32_t lParam)
GetMousePos(lParam);
if (sgbMouseDown == CLICK_NONE) {
sgbMouseDown = CLICK_LEFT;
track_repeat_walk(LeftMouseDown(wParam));
LeftMouseDown(wParam);
}
return;
case DVL_WM_LBUTTONUP:
@ -743,7 +742,6 @@ void GameEventHandler(uint32_t uMsg, int32_t wParam, int32_t lParam)
LastMouseButtonAction = MouseActionType::None;
sgbMouseDown = CLICK_NONE;
LeftMouseUp(wParam);
track_repeat_walk(false);
}
return;
case DVL_WM_RBUTTONDOWN:
@ -762,7 +760,7 @@ void GameEventHandler(uint32_t uMsg, int32_t wParam, int32_t lParam)
return;
case DVL_WM_CAPTURECHANGED:
sgbMouseDown = CLICK_NONE;
track_repeat_walk(false);
LastMouseButtonAction = MouseActionType::None;
break;
case WM_DIABNEXTLVL:
case WM_DIABPREVLVL:
@ -778,7 +776,7 @@ void GameEventHandler(uint32_t uMsg, int32_t wParam, int32_t lParam)
PaletteFadeOut(8);
sound_stop();
music_stop();
track_repeat_walk(false);
LastMouseButtonAction = MouseActionType::None;
sgbMouseDown = CLICK_NONE;
ShowProgress((interface_mode)uMsg);
force_redraw = 255;
@ -1298,7 +1296,7 @@ void HelpKeyPressed()
ClearPanel();
AddPanelString(_("No help available")); /// BUGFIX: message isn't displayed
AddPanelString(_("while in stores"));
track_repeat_walk(false);
LastMouseButtonAction = MouseActionType::None;
} else {
invflag = false;
chrflag = false;
@ -1408,7 +1406,7 @@ void DisplaySpellsKeyPressed()
} else {
spselflag = false;
}
track_repeat_walk(false);
LastMouseButtonAction = MouseActionType::None;
}
void SpellBookKeyPressed()
@ -1757,7 +1755,7 @@ void diablo_pause_game()
} else {
PauseMode = 2;
sound_stop();
track_repeat_walk(false);
LastMouseButtonAction = MouseActionType::None;
}
force_redraw = 255;
@ -1778,7 +1776,7 @@ void diablo_focus_pause()
if (!GameWasAlreadyPaused) {
PauseMode = 2;
sound_stop();
track_repeat_walk(false);
LastMouseButtonAction = MouseActionType::None;
}
#ifndef NOSOUND

4
Source/diablo.h

@ -45,8 +45,10 @@ enum class GameLogicStep {
enum class MouseActionType : int {
None,
Walk,
Spell,
SpellOutOfMana,
SpellMonsterTarget,
SpellPlayerTarget,
Attack,
AttackMonsterTarget,
AttackPlayerTarget,

2
Source/objects.cpp

@ -1474,7 +1474,7 @@ void UpdateCircle(int i)
if (Quests[Q_BETRAYER]._qactive == QUEST_ACTIVE && Quests[Q_BETRAYER]._qvar1 <= 4) // BUGFIX stepping on the circle again will break the quest state (fixed)
Quests[Q_BETRAYER]._qvar1 = 4;
AddMissile(myPlayer.position.tile, { 35, 46 }, myPlayer._pdir, MIS_RNDTELEPORT, TARGET_MONSTERS, MyPlayerId, 0, 0);
track_repeat_walk(false);
LastMouseButtonAction = MouseActionType::None;
sgbMouseDown = CLICK_NONE;
ClrPlrPath(myPlayer);
StartStand(MyPlayerId, DIR_S);

25
Source/player.cpp

@ -2167,14 +2167,7 @@ void PlayerStruct::Stop()
bool PlayerStruct::IsWalking() const
{
switch (_pmode) {
case PM_WALK:
case PM_WALK2:
case PM_WALK3:
return true;
default:
return false;
}
return IsAnyOf(_pmode, PM_WALK, PM_WALK2, PM_WALK3);
}
void PlayerStruct::Reset()
@ -3549,7 +3542,7 @@ void CalcPlrStaff(PlayerStruct &player)
}
}
void CheckPlrSpell(bool mouseClick)
void CheckPlrSpell()
{
bool addflag = false;
int sl;
@ -3607,30 +3600,30 @@ void CheckPlrSpell(bool mouseClick)
if (!addflag) {
if (myPlayer._pRSplType == RSPLTYPE_SPELL) {
if (!mouseClick || LastMouseButtonAction != MouseActionType::SpellOutOfMana)
myPlayer.Say(HeroSpeech::NotEnoughMana);
if (mouseClick)
LastMouseButtonAction = MouseActionType::SpellOutOfMana;
myPlayer.Say(HeroSpeech::NotEnoughMana);
LastMouseButtonAction = MouseActionType::None;
}
return;
}
if (myPlayer._pRSpell == SPL_FIREWALL || myPlayer._pRSpell == SPL_LIGHTWALL) {
LastMouseButtonAction = MouseActionType::Spell;
Direction sd = GetDirection(myPlayer.position.tile, { cursmx, cursmy });
sl = GetSpellLevel(MyPlayerId, myPlayer._pRSpell);
NetSendCmdLocParam3(true, CMD_SPELLXYD, { cursmx, cursmy }, myPlayer._pRSpell, sd, sl);
} else if (pcursmonst != -1) {
LastMouseButtonAction = MouseActionType::SpellMonsterTarget;
sl = GetSpellLevel(MyPlayerId, myPlayer._pRSpell);
NetSendCmdParam3(true, CMD_SPELLID, pcursmonst, myPlayer._pRSpell, sl);
} else if (pcursplr != -1) {
LastMouseButtonAction = MouseActionType::SpellPlayerTarget;
sl = GetSpellLevel(MyPlayerId, myPlayer._pRSpell);
NetSendCmdParam3(true, CMD_SPELLPID, pcursplr, myPlayer._pRSpell, sl);
} else { //145
} else {
LastMouseButtonAction = MouseActionType::Spell;
sl = GetSpellLevel(MyPlayerId, myPlayer._pRSpell);
NetSendCmdLocParam2(true, CMD_SPELLXY, { cursmx, cursmy }, myPlayer._pRSpell, sl);
}
if (mouseClick)
LastMouseButtonAction = MouseActionType::Spell;
}
void SyncPlrAnim(int pnum)

19
Source/player.h

@ -433,7 +433,22 @@ struct PlayerStruct {
bool UsesRangedWeapon() const
{
return static_cast<PlayerWeaponGraphic>(_pgfxnum & 0xF) == PlayerWeaponGraphic::Bow;
};
}
bool CanChangeAction()
{
if (_pmode == PM_STAND)
return true;
if (_pmode == PM_ATTACK && AnimInfo.CurrentFrame > _pAFNum)
return true;
if (_pmode == PM_RATTACK && AnimInfo.CurrentFrame > _pAFNum)
return true;
if (_pmode == PM_SPELL && AnimInfo.CurrentFrame > _pSFNum)
return true;
if (IsWalking() && AnimInfo.CurrentFrame == AnimInfo.NumberOfFrames)
return true;
return false;
}
};
extern int MyPlayerId;
@ -488,7 +503,7 @@ void ClrPlrPath(PlayerStruct &player);
bool PosOkPlayer(const PlayerStruct &player, Point position);
void MakePlrPath(PlayerStruct &player, Point targetPosition, bool endspace);
void CalcPlrStaff(PlayerStruct &player);
void CheckPlrSpell(bool mouseClick);
void CheckPlrSpell();
void SyncPlrAnim(int pnum);
void SyncInitPlrPos(int pnum);
void SyncInitPlr(int pnum);

108
Source/track.cpp

@ -15,28 +15,41 @@ namespace devilution {
namespace {
bool sgbIsScrolling;
uint32_t sgdwLastWalk;
bool sgbIsWalking;
void RepeatWalk(PlayerStruct &player)
{
if (cursmx < 0 || cursmx >= MAXDUNX - 1 || cursmy < 0 || cursmy >= MAXDUNY - 1)
return;
if (player._pmode != PM_STAND && !(player.IsWalking() && player.AnimInfo.GetFrameToUseForRendering() > 6))
return;
const Point target = player.GetTargetPosition();
if (cursmx == target.x && cursmy == target.y)
return;
NetSendCmdLoc(MyPlayerId, true, CMD_WALKXY, { cursmx, cursmy });
}
} // namespace
bool RepeatMouseAction()
void RepeatMouseAction()
{
if (pcurs != CURSOR_HAND)
return false;
return;
if (sgbMouseDown == CLICK_NONE)
return false;
return;
if (LastMouseButtonAction == MouseActionType::None)
return;
auto &myPlayer = Players[MyPlayerId];
if (myPlayer._pmode == PM_DEATH
|| myPlayer._pmode == PM_QUIT
|| myPlayer.destAction != ACTION_NONE
|| SDL_GetTicks() - LastMouseButtonTime < gnTickDelay * 4) {
return true;
}
if (myPlayer.destAction != ACTION_NONE)
return;
if (!myPlayer.CanChangeAction())
return;
bool rangedAttack = myPlayer.UsesRangedWeapon();
LastMouseButtonTime = SDL_GetTicks();
switch (LastMouseButtonAction) {
case MouseActionType::Attack:
if (cursmx >= 0 && cursmx < MAXDUNX && cursmy >= 0 && cursmy < MAXDUNY)
@ -51,8 +64,15 @@ bool RepeatMouseAction()
NetSendCmdParam1(true, rangedAttack ? CMD_RATTACKPID : CMD_ATTACKPID, pcursplr);
break;
case MouseActionType::Spell:
case MouseActionType::SpellOutOfMana:
CheckPlrSpell(true);
CheckPlrSpell();
break;
case MouseActionType::SpellMonsterTarget:
if (pcursmonst != -1)
CheckPlrSpell();
break;
case MouseActionType::SpellPlayerTarget:
if (pcursplr != -1 && !gbFriendlyMode)
CheckPlrSpell();
break;
case MouseActionType::OperateObject:
if (pcursobj != -1) {
@ -62,65 +82,17 @@ bool RepeatMouseAction()
NetSendCmdLocParam1(true, CMD_OPOBJXY, object.position, pcursobj);
}
break;
case MouseActionType::Other:
case MouseActionType::Walk:
RepeatWalk(myPlayer);
break;
case MouseActionType::None:
return false;
}
return true;
}
} // namespace
void track_process()
{
if (RepeatMouseAction())
return;
if (!sgbIsWalking)
return;
if (cursmx < 0 || cursmx >= MAXDUNX - 1 || cursmy < 0 || cursmy >= MAXDUNY - 1)
return;
const auto &player = Players[MyPlayerId];
if (player._pmode != PM_STAND && !(player.IsWalking() && player.AnimInfo.GetFrameToUseForRendering() > 6))
return;
const Point target = player.GetTargetPosition();
if (cursmx != target.x || cursmy != target.y) {
uint32_t tick = SDL_GetTicks();
int tickMultiplier = 6;
if (currlevel == 0 && sgGameInitInfo.bRunInTown != 0)
tickMultiplier = 3;
if ((int)(tick - sgdwLastWalk) >= gnTickDelay * tickMultiplier) {
sgdwLastWalk = tick;
NetSendCmdLoc(MyPlayerId, true, CMD_WALKXY, { cursmx, cursmy });
if (!sgbIsScrolling)
sgbIsScrolling = true;
}
}
}
void track_repeat_walk(bool rep)
{
if (sgbIsWalking == rep)
return;
sgbIsWalking = rep;
if (rep) {
sgbIsScrolling = false;
sgdwLastWalk = SDL_GetTicks() - gnTickDelay;
NetSendCmdLoc(MyPlayerId, true, CMD_WALKXY, { cursmx, cursmy });
} else if (sgbIsScrolling) {
sgbIsScrolling = false;
break;
}
}
bool track_isscrolling()
{
return sgbIsScrolling;
return LastMouseButtonAction == MouseActionType::Walk;
}
} // namespace devilution

3
Source/track.h

@ -7,8 +7,7 @@
namespace devilution {
void track_process();
void track_repeat_walk(bool rep);
void RepeatMouseAction();
bool track_isscrolling();
} // namespace devilution

Loading…
Cancel
Save