Skip to content

Commit

Permalink
feat: auto-turn to selected target (#1588)
Browse files Browse the repository at this point in the history
* feat: auto-turn to selected target

* Implements a new targeting game setting that allows players to auto-turn to the selected target when enabled.

* Player will auto-turn to their currently selected target (by clicking on them or switching targets) only when not moving and if the direction needs to be changed from the current one.

* The feature can be toggled by the server config as well. When disabled, it will make it so the feature no longer works for the client and the checkbox within the game settings is disabled and unchecked.

* Minor chore at DrawTargets() : reduced nesting by merging and inverting if(s) and used conditional en.Value != null within the foreach loops required for this feature.

* Special thanks to Daywalkr (Middle Ages: Online), took (and changed a bit) the implementation from his repository.

* Panda's review (I)

* Panda's review (II)

* panda's review.
* created an entity property (long) "AutoTargetingTime" to limit down the direction change packet sender in order to prevent burst packets from the client (tested with a non-admin character to reproduce the packet flood).
* the delay added by AutoTargetingTime also allows to prevent a visual glitch encountered while testing with two clients and a walking/targeting player: now, when auto-targeting right after the player stops walking, the extra delay allows the character to turn around to it's target more naturally (to every player view - local and other clients) when they stop moving.
* checks if the current facing direction is the direction towards the target, in which case (if true), returns (stops the loop) without further processing in order to prevent unnecessary burst of packets and processing from the client.

* chore: adds server configurable property for delay

* Also fixed some typos and re-structured AutoTurnToTarget(); a bit.

* Panda's review (III)
  • Loading branch information
Arufonsu committed Oct 14, 2022
1 parent ea9361b commit 60e6254
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 18 deletions.
10 changes: 10 additions & 0 deletions Intersect (Core)/Config/PlayerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ public partial class PlayerOptions
/// </summary>
public bool AllowCombatMovement { get; set; } = true;

/// <summary>
/// Enables the client feature 'Auto-turn to target'.
/// </summary>
public bool EnableAutoTurnToTarget { get; set; } = true;

/// <summary>
/// Sets the delay (milliseconds) for the feature 'Auto-turn to target'.
/// </summary>
public ushort AutoTurnToTargetDelay { get; set; } = 500;

/// <summary>
/// Enables or disables friend login notifications when a user joins the game.
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions Intersect.Client.Framework/Database/GameDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public abstract partial class GameDatabase

public bool StickyTarget { get; set; }

public bool AutoTurnToTarget { get; set; }

public bool FriendOverheadInfo { get; set; }

public bool GuildMemberOverheadInfo { get; set; }
Expand Down Expand Up @@ -77,6 +79,7 @@ public virtual void LoadPreferences()
HideOthersOnWindowOpen = LoadPreference(nameof(HideOthersOnWindowOpen), true);
TargetAccountDirection = LoadPreference(nameof(TargetAccountDirection), false);
StickyTarget = LoadPreference(nameof(StickyTarget), false);
AutoTurnToTarget = LoadPreference(nameof(AutoTurnToTarget), false);
FriendOverheadInfo = LoadPreference(nameof(FriendOverheadInfo), true);
GuildMemberOverheadInfo = LoadPreference(nameof(GuildMemberOverheadInfo), true);
MyOverheadInfo = LoadPreference(nameof(MyOverheadInfo), true);
Expand All @@ -102,6 +105,7 @@ public virtual void SavePreferences()
SavePreference(nameof(HideOthersOnWindowOpen), HideOthersOnWindowOpen);
SavePreference(nameof(TargetAccountDirection), TargetAccountDirection);
SavePreference(nameof(StickyTarget), StickyTarget);
SavePreference(nameof(AutoTurnToTarget), AutoTurnToTarget);
SavePreference(nameof(FriendOverheadInfo), FriendOverheadInfo);
SavePreference(nameof(GuildMemberOverheadInfo), GuildMemberOverheadInfo);
SavePreference(nameof(MyOverheadInfo), MyOverheadInfo);
Expand Down
54 changes: 54 additions & 0 deletions Intersect.Client/Entities/Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ public Guid SpellCast
public long SpriteFrameTimer { get; set; } = -1;

public long LastActionTime { get; set; } = -1;

public long AutoTurnToTargetTimer { get; set; } = -1;

#endregion

public EntityTypes Type { get; }
Expand Down Expand Up @@ -1878,6 +1881,57 @@ protected virtual bool TryGetAnimationTexture(string textureName, SpriteAnimatio
return texture != default;
}

/// <summary>
/// <para>Returns the direction to a player's selected target.
/// Thanks to Daywalkr (Middle Ages: Online) for this !</para>
/// </summary>
/// <param name="en">entity's target</param>
/// <returns>direction to player's selected target</returns>
protected byte DirectionToTarget(Entity en)
{
byte newDir = Dir;

if (en == null)
{
return newDir;
}

int originY = Y;
int originX = X;
int targetY = en.Y;
int targetX = en.X;

if (en.MapInstance.Id != MapInstance.Id)
{
if (en.MapInstance.GridY < MapInstance.GridY)
{
originY += Options.MapHeight - 1;
}
else if (en.MapInstance.GridY > MapInstance.GridY)
{
targetY += Options.MapHeight - 1;
}

if (en.MapInstance.GridX < MapInstance.GridX)
{
originX += Options.MapWidth - 1;
}
else if (en.MapInstance.GridX > MapInstance.GridX)
{
targetX += (Options.MapWidth - 1);
}
}

var yOffset = originY - targetY;
var xOffset = originX - targetX;
var xDir = xOffset == 0 ? newDir : (xOffset < 0 ? (byte)Directions.Right : (byte)Directions.Left);
var yDir = yOffset == 0 ? newDir : (yOffset < 0 ? (byte)Directions.Down : (byte)Directions.Up);

newDir = Math.Abs(yOffset) > Math.Abs(xOffset) ? yDir : xDir;

return newDir;
}

//Movement
/// <summary>
/// Returns -6 if the tile is blocked by a global (non-event) entity
Expand Down
86 changes: 70 additions & 16 deletions Intersect.Client/Entities/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1507,6 +1507,31 @@ private void SetTargetBox(Entity en)
TargetBox?.Show();
}

private void AutoTurnToTarget(Entity en)
{
if (!Globals.Database.AutoTurnToTarget || !Options.Instance.PlayerOpts.EnableAutoTurnToTarget)
{
return;
}

byte directionToTarget = DirectionToTarget(en);

if (IsMoving || Dir == MoveDir || Dir == directionToTarget)
{
AutoTurnToTargetTimer = Timing.Global.Milliseconds + Options.Instance.PlayerOpts.AutoTurnToTargetDelay;
return;
}

if (AutoTurnToTargetTimer > Timing.Global.Milliseconds)
{
return;
}

MoveDir = -1;
Dir = directionToTarget;
PacketSender.SendDirection(Dir);
}

public bool TryBlock()
{
if (IsBlocking)
Expand Down Expand Up @@ -2379,16 +2404,28 @@ public void DrawTargets()
continue;
}

if (!en.Value.IsHidden && (!en.Value.IsStealthed || en.Value is Player player && Globals.Me.IsInMyParty(player)))
if (en.Value.IsHidden)
{
if (!(en.Value is Projectile || en.Value is Resource))
{
if (TargetType == 0 && TargetIndex == en.Value.Id)
{
en.Value.DrawTarget((int)TargetTypes.Selected);
}
}
continue;
}

if (en.Value.IsStealthed && (!(en.Value is Player player) || !Globals.Me.IsInMyParty(player)))
{
continue;
}

if (en.Value is Projectile || en.Value is Resource)
{
continue;
}

if (TargetType != 0 || TargetIndex != en.Value.Id)
{
continue;
}

en.Value.DrawTarget((int)TargetTypes.Selected);
AutoTurnToTarget(en.Value);
}

foreach (MapInstance eventMap in Maps.MapInstance.Lookup.Values)
Expand All @@ -2400,16 +2437,33 @@ public void DrawTargets()
continue;
}

if (en.Value.MapId == eventMap.Id &&
!((Event)en.Value).DisablePreview &&
!en.Value.IsHidden &&
(!en.Value.IsStealthed || en.Value is Player player && Globals.Me.IsInMyParty(player)))
if (en.Value.MapId != eventMap.Id)
{
if (TargetType == 1 && TargetIndex == en.Value.Id)
{
en.Value.DrawTarget((int)TargetTypes.Selected);
}
continue;
}

if (en.Value is Event eventEntity && eventEntity.DisablePreview)
{
continue;
}

if (en.Value.IsHidden)
{
continue;
}

if (en.Value.IsStealthed && (!(en.Value is Player player) || !Globals.Me.IsInMyParty(player)))
{
continue;
}

if (TargetType != 1 || TargetIndex != en.Value.Id)
{
continue;
}

en.Value.DrawTarget((int)TargetTypes.Selected);
AutoTurnToTarget(en.Value);
}
}

Expand Down
13 changes: 11 additions & 2 deletions Intersect.Client/Interface/Shared/SettingsWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public partial class SettingsWindow

private readonly LabeledCheckBox mStickyTarget;

private readonly LabeledCheckBox mAutoTurnToTarget;

// Video Settings.
private readonly ComboBox mResolutionList;

Expand Down Expand Up @@ -229,6 +231,10 @@ public SettingsWindow(Base parent, MainMenu mainMenu, EscapeMenu escapeMenu)
mStickyTarget = new LabeledCheckBox(mTargetingSettings, "StickyTargetCheckbox");
mStickyTarget.Text = Strings.Settings.StickyTarget;

// Game Settings - Targeting: Auto-turn to Target.
mAutoTurnToTarget = new LabeledCheckBox(mTargetingSettings, "AutoTurnToTargetCheckbox");
mAutoTurnToTarget.Text = Strings.Settings.AutoTurnToTarget;

#endregion

#region InitVideoSettings
Expand Down Expand Up @@ -415,19 +421,20 @@ void InterfaceSettings_Clicked(Base sender, ClickedEventArgs arguments)
mInformationSettings.Hide();
mTargetingSettings.Hide();
}

void InformationSettings_Clicked(Base sender, ClickedEventArgs arguments)
{
mInterfaceSettings.Hide();
mInformationSettings.Show();
mTargetingSettings.Hide();
}

void TargetingSettings_Clicked(Base sender, ClickedEventArgs arguments)
{
mInterfaceSettings.Hide();
mInformationSettings.Hide();
mTargetingSettings.Show();
mAutoTurnToTarget.IsDisabled = !Options.Instance.PlayerOpts.EnableAutoTurnToTarget;
}

private void VideoSettingsTab_Clicked(Base sender, ClickedEventArgs arguments)
Expand Down Expand Up @@ -627,6 +634,7 @@ public void Show(bool returnToMenu = false)
mPartyMemberOverheadInfoCheckbox.IsChecked = Globals.Database.PartyMemberOverheadInfo;
mPlayerOverheadInfoCheckbox.IsChecked = Globals.Database.PlayerOverheadInfo;
mStickyTarget.IsChecked = Globals.Database.StickyTarget;
mAutoTurnToTarget.IsChecked = Globals.Database.AutoTurnToTarget;

// Video Settings.
mFullscreenCheckbox.IsChecked = Globals.Database.FullScreen;
Expand Down Expand Up @@ -799,6 +807,7 @@ private void SettingsApplyBtn_Clicked(Base sender, ClickedEventArgs arguments)
Globals.Database.PartyMemberOverheadInfo = mPartyMemberOverheadInfoCheckbox.IsChecked;
Globals.Database.PlayerOverheadInfo = mPlayerOverheadInfoCheckbox.IsChecked;
Globals.Database.StickyTarget = mStickyTarget.IsChecked;
Globals.Database.AutoTurnToTarget = mAutoTurnToTarget.IsChecked;

// Video Settings.
var resolution = mResolutionList.SelectedItem;
Expand Down
3 changes: 3 additions & 0 deletions Intersect.Client/Localization/Strings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1787,6 +1787,9 @@ public partial struct Settings
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public static LocalizedString TargetingSettings = @"Targeting";

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public static LocalizedString AutoTurnToTarget = @"Auto-turn to target";

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public static LocalizedString Title = @"Settings";

Expand Down

0 comments on commit 60e6254

Please sign in to comment.