stuff
This commit is contained in:
parent
a8341e8232
commit
a8c43ba7e2
43 changed files with 2618 additions and 48 deletions
|
|
@ -2,7 +2,7 @@ using System.Numerics;
|
|||
|
||||
namespace Roboto.Core;
|
||||
|
||||
public enum ClickType { Left, Right }
|
||||
public enum ClickType { Left, Right, Middle }
|
||||
public enum KeyActionType { Press, Down, Up }
|
||||
|
||||
public abstract record BotAction(int Priority);
|
||||
|
|
|
|||
|
|
@ -14,10 +14,6 @@ public class BotConfig
|
|||
// Navigation
|
||||
public float WorldToGrid { get; set; } = 23f / 250f;
|
||||
|
||||
// Combat
|
||||
public float CriticalHpPercent { get; set; } = 30f;
|
||||
public float LowHpPercent { get; set; } = 50f;
|
||||
|
||||
// Loot
|
||||
public float LootPickupRange { get; set; } = 600f;
|
||||
|
||||
|
|
@ -30,11 +26,4 @@ public class BotConfig
|
|||
|
||||
// Anti-detection
|
||||
public float PollIntervalJitter { get; set; } = 0.2f;
|
||||
|
||||
// Flasks
|
||||
public float LifeFlaskThreshold { get; set; } = 50f;
|
||||
public float ManaFlaskThreshold { get; set; } = 50f;
|
||||
public int FlaskCooldownMs { get; set; } = 4000;
|
||||
public ushort LifeFlaskScanCode { get; set; } = 0x02; // Key1
|
||||
public ushort ManaFlaskScanCode { get; set; } = 0x03; // Key2
|
||||
}
|
||||
|
|
|
|||
24
src/Roboto.Core/CharacterProfile.cs
Normal file
24
src/Roboto.Core/CharacterProfile.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public class CharacterProfile
|
||||
{
|
||||
public string Name { get; set; } = "Default";
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime LastModified { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public FlaskSettings Flasks { get; set; } = new();
|
||||
public CombatSettings Combat { get; set; } = new();
|
||||
public List<SkillProfile> Skills { get; set; } = DefaultSkills();
|
||||
|
||||
public static List<SkillProfile> DefaultSkills() =>
|
||||
[
|
||||
new() { SlotIndex = 0, Label = "LMB", InputType = SkillInputType.LeftClick, Priority = 0 },
|
||||
new() { SlotIndex = 1, Label = "RMB", InputType = SkillInputType.RightClick, Priority = 1 },
|
||||
new() { SlotIndex = 2, Label = "MMB", InputType = SkillInputType.MiddleClick, Priority = 2 },
|
||||
new() { SlotIndex = 3, Label = "Q", InputType = SkillInputType.KeyPress, ScanCode = 0x10, Priority = 3 },
|
||||
new() { SlotIndex = 4, Label = "E", InputType = SkillInputType.KeyPress, ScanCode = 0x12, Priority = 4 },
|
||||
new() { SlotIndex = 5, Label = "R", InputType = SkillInputType.KeyPress, ScanCode = 0x13, Priority = 5 },
|
||||
new() { SlotIndex = 6, Label = "T", InputType = SkillInputType.KeyPress, ScanCode = 0x14, Priority = 6 },
|
||||
new() { SlotIndex = 7, Label = "F", InputType = SkillInputType.KeyPress, ScanCode = 0x21, Priority = 7 },
|
||||
];
|
||||
}
|
||||
11
src/Roboto.Core/CombatSettings.cs
Normal file
11
src/Roboto.Core/CombatSettings.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public class CombatSettings
|
||||
{
|
||||
public int GlobalCooldownMs { get; set; } = 500;
|
||||
public float AttackRange { get; set; } = 600f;
|
||||
public float SafeRange { get; set; } = 400f;
|
||||
public bool KiteEnabled { get; set; }
|
||||
public float KiteRange { get; set; } = 300f;
|
||||
public int KiteDelayMs { get; set; } = 200;
|
||||
}
|
||||
|
|
@ -58,6 +58,7 @@ public record EntitySnapshot
|
|||
public MonsterRarity Rarity { get; init; }
|
||||
public List<string>? ModNames { get; init; }
|
||||
public string? TransitionName { get; init; }
|
||||
public int TransitionState { get; init; } = -1;
|
||||
public string? Metadata { get; init; }
|
||||
|
||||
// Action state (from Actor component)
|
||||
|
|
|
|||
10
src/Roboto.Core/FlaskSettings.cs
Normal file
10
src/Roboto.Core/FlaskSettings.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public class FlaskSettings
|
||||
{
|
||||
public float LifeFlaskThreshold { get; set; } = 50f;
|
||||
public float ManaFlaskThreshold { get; set; } = 50f;
|
||||
public int FlaskCooldownMs { get; set; } = 4000;
|
||||
public ushort LifeFlaskScanCode { get; set; } = 0x02; // Key1
|
||||
public ushort ManaFlaskScanCode { get; set; } = 0x03; // Key2
|
||||
}
|
||||
|
|
@ -16,10 +16,12 @@ public class GameState
|
|||
|
||||
public uint AreaHash { get; set; }
|
||||
public int AreaLevel { get; set; }
|
||||
public string? CurrentAreaName { get; set; }
|
||||
public bool IsLoading { get; set; }
|
||||
public bool IsEscapeOpen { get; set; }
|
||||
public DangerLevel Danger { get; set; }
|
||||
public Matrix4x4? CameraMatrix { get; set; }
|
||||
public IReadOnlyList<QuestProgress> ActiveQuests { get; set; } = [];
|
||||
|
||||
// Derived (computed once per tick by GameStateEnricher)
|
||||
public ThreatMap Threats { get; set; } = new();
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ public interface IInputController
|
|||
void KeyUp(ushort scanCode);
|
||||
void KeyPress(ushort scanCode, int holdMs = 50);
|
||||
void MouseMoveTo(int x, int y);
|
||||
void SmoothMoveTo(int x, int y);
|
||||
void MouseMoveBy(int dx, int dy);
|
||||
void LeftClick(int x, int y);
|
||||
void RightClick(int x, int y);
|
||||
void MiddleClick(int x, int y);
|
||||
void LeftDown();
|
||||
void LeftUp();
|
||||
void RightDown();
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace Roboto.Core;
|
|||
|
||||
public record PlayerState
|
||||
{
|
||||
public string? CharacterName { get; init; }
|
||||
public Vector2 Position { get; init; }
|
||||
public float Z { get; init; }
|
||||
public bool HasPosition { get; init; }
|
||||
|
|
@ -19,6 +20,9 @@ public record PlayerState
|
|||
public float ManaPercent => ManaTotal > 0 ? (float)ManaCurrent / ManaTotal * 100f : 0f;
|
||||
public float EsPercent => EsTotal > 0 ? (float)EsCurrent / EsTotal * 100f : 0f;
|
||||
|
||||
// Action state (from Actor component)
|
||||
public short ActionId { get; init; }
|
||||
|
||||
// Flask state (populated by memory when available)
|
||||
public IReadOnlyList<FlaskState> Flasks { get; init; } = [];
|
||||
|
||||
|
|
|
|||
174
src/Roboto.Core/ProfileManager.cs
Normal file
174
src/Roboto.Core/ProfileManager.cs
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Roboto.Core;
|
||||
|
||||
public sealed class ProfileManager
|
||||
{
|
||||
private static readonly JsonSerializerOptions JsonOpts = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
Converters = { new JsonStringEnumConverter() },
|
||||
};
|
||||
|
||||
private readonly string _profilesDir;
|
||||
private readonly string _assignmentsFile;
|
||||
private readonly object _lock = new();
|
||||
private Dictionary<string, string> _assignments = new(); // charName → profileName
|
||||
|
||||
public ProfileManager(string profilesDir = "profiles")
|
||||
{
|
||||
_profilesDir = Path.GetFullPath(profilesDir);
|
||||
_assignmentsFile = Path.Combine(_profilesDir, "_assignments.json");
|
||||
EnsureDirectory();
|
||||
LoadAssignments();
|
||||
}
|
||||
|
||||
public CharacterProfile LoadForCharacter(string charName)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_assignments.TryGetValue(charName, out var profileName))
|
||||
{
|
||||
var profile = LoadProfile(profileName);
|
||||
if (profile is not null)
|
||||
return profile;
|
||||
}
|
||||
|
||||
// Create default profile for this character
|
||||
var defaultName = $"{charName}_Default";
|
||||
var defaultProfile = new CharacterProfile { Name = defaultName };
|
||||
SaveProfile(defaultProfile);
|
||||
_assignments[charName] = defaultName;
|
||||
SaveAssignments();
|
||||
return defaultProfile;
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(CharacterProfile profile)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
profile.LastModified = DateTime.UtcNow;
|
||||
SaveProfile(profile);
|
||||
}
|
||||
}
|
||||
|
||||
public CharacterProfile? Duplicate(string profileName, string newName)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var source = LoadProfile(profileName);
|
||||
if (source is null) return null;
|
||||
|
||||
source.Name = newName;
|
||||
source.CreatedAt = DateTime.UtcNow;
|
||||
source.LastModified = DateTime.UtcNow;
|
||||
SaveProfile(source);
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
public void AssignToCharacter(string charName, string profileName)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_assignments[charName] = profileName;
|
||||
SaveAssignments();
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(string profileName)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var path = ProfilePath(profileName);
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
|
||||
// Remove any assignments pointing to this profile
|
||||
var toRemove = _assignments
|
||||
.Where(kv => kv.Value == profileName)
|
||||
.Select(kv => kv.Key)
|
||||
.ToList();
|
||||
foreach (var key in toRemove)
|
||||
_assignments.Remove(key);
|
||||
if (toRemove.Count > 0)
|
||||
SaveAssignments();
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> ListProfiles()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
EnsureDirectory();
|
||||
return Directory.GetFiles(_profilesDir, "*.json")
|
||||
.Select(Path.GetFileNameWithoutExtension)
|
||||
.Where(n => n is not null && !n.StartsWith("_"))
|
||||
.Cast<string>()
|
||||
.OrderBy(n => n)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public string? GetAssignment(string charName)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _assignments.GetValueOrDefault(charName);
|
||||
}
|
||||
}
|
||||
|
||||
private CharacterProfile? LoadProfile(string name)
|
||||
{
|
||||
var path = ProfilePath(name);
|
||||
if (!File.Exists(path)) return null;
|
||||
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(path);
|
||||
return JsonSerializer.Deserialize<CharacterProfile>(json, JsonOpts);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveProfile(CharacterProfile profile)
|
||||
{
|
||||
EnsureDirectory();
|
||||
var json = JsonSerializer.Serialize(profile, JsonOpts);
|
||||
File.WriteAllText(ProfilePath(profile.Name), json);
|
||||
}
|
||||
|
||||
private void LoadAssignments()
|
||||
{
|
||||
if (!File.Exists(_assignmentsFile)) return;
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(_assignmentsFile);
|
||||
_assignments = JsonSerializer.Deserialize<Dictionary<string, string>>(json, JsonOpts) ?? new();
|
||||
}
|
||||
catch
|
||||
{
|
||||
_assignments = new();
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveAssignments()
|
||||
{
|
||||
EnsureDirectory();
|
||||
var json = JsonSerializer.Serialize(_assignments, JsonOpts);
|
||||
File.WriteAllText(_assignmentsFile, json);
|
||||
}
|
||||
|
||||
private void EnsureDirectory()
|
||||
{
|
||||
if (!Directory.Exists(_profilesDir))
|
||||
Directory.CreateDirectory(_profilesDir);
|
||||
}
|
||||
|
||||
private string ProfilePath(string name) => Path.Combine(_profilesDir, $"{name}.json");
|
||||
}
|
||||
9
src/Roboto.Core/QuestProgress.cs
Normal file
9
src/Roboto.Core/QuestProgress.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public record QuestProgress
|
||||
{
|
||||
public string? QuestName { get; init; }
|
||||
public byte StateId { get; init; }
|
||||
public string? StateText { get; init; }
|
||||
public string? ProgressText { get; init; }
|
||||
}
|
||||
25
src/Roboto.Core/SkillProfile.cs
Normal file
25
src/Roboto.Core/SkillProfile.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public enum SkillInputType { KeyPress, LeftClick, RightClick, MiddleClick }
|
||||
|
||||
public class SkillProfile
|
||||
{
|
||||
public int SlotIndex { get; set; }
|
||||
public string Label { get; set; } = "";
|
||||
public string? SkillName { get; set; }
|
||||
public SkillInputType InputType { get; set; }
|
||||
public ushort ScanCode { get; set; }
|
||||
public int Priority { get; set; }
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
public int CooldownMs { get; set; } = 300;
|
||||
public float RangeMin { get; set; }
|
||||
public float RangeMax { get; set; } = 600f;
|
||||
|
||||
public TargetSelection TargetSelection { get; set; } = TargetSelection.Nearest;
|
||||
public bool RequiresTarget { get; set; } = true;
|
||||
public bool IsAura { get; set; }
|
||||
public bool IsMovementSkill { get; set; }
|
||||
public int MinMonstersInRange { get; set; } = 1;
|
||||
public bool MaintainPressed { get; set; }
|
||||
}
|
||||
11
src/Roboto.Core/TargetSelection.cs
Normal file
11
src/Roboto.Core/TargetSelection.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public enum TargetSelection
|
||||
{
|
||||
Nearest,
|
||||
All,
|
||||
Rarest,
|
||||
MagicPlus,
|
||||
RarePlus,
|
||||
UniqueOnly,
|
||||
}
|
||||
33
src/Roboto.Core/WorldToScreen.cs
Normal file
33
src/Roboto.Core/WorldToScreen.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace Roboto.Core;
|
||||
|
||||
public static class WorldToScreen
|
||||
{
|
||||
private const float HalfW = 1280f; // 2560 / 2
|
||||
private const float HalfH = 720f; // 1440 / 2
|
||||
|
||||
/// <summary>
|
||||
/// Projects a world position to screen coordinates using the camera's view-projection matrix.
|
||||
/// Returns null if the point is behind the camera or off screen.
|
||||
/// </summary>
|
||||
public static Vector2? Project(Vector2 worldPos, float z, Matrix4x4 cameraMatrix)
|
||||
{
|
||||
var world = new Vector4(worldPos.X, worldPos.Y, z, 1f);
|
||||
var clip = Vector4.Transform(world, cameraMatrix);
|
||||
|
||||
if (clip.W is 0f or float.NaN)
|
||||
return null;
|
||||
|
||||
clip = Vector4.Divide(clip, clip.W);
|
||||
|
||||
var sx = (clip.X + 1f) * HalfW;
|
||||
var sy = (1f - clip.Y) * HalfH;
|
||||
|
||||
// Off-screen check
|
||||
if (sx < 0 || sx > 2560 || sy < 0 || sy > 1440)
|
||||
return null;
|
||||
|
||||
return new Vector2(sx, sy);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue