lots done
This commit is contained in:
parent
1ba7c39c30
commit
fbd0ba445a
59 changed files with 6074 additions and 3598 deletions
81
src/Roboto.Core/ActionQueue.cs
Normal file
81
src/Roboto.Core/ActionQueue.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public class ActionQueue
|
||||
{
|
||||
private readonly List<BotAction> _actions = [];
|
||||
|
||||
public IReadOnlyList<BotAction> Actions => _actions;
|
||||
|
||||
public void Submit(BotAction action)
|
||||
{
|
||||
// Bump lower-priority same-type actions
|
||||
for (var i = _actions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_actions[i].GetType() == action.GetType() && _actions[i].Priority >= action.Priority)
|
||||
_actions.RemoveAt(i);
|
||||
}
|
||||
_actions.Add(action);
|
||||
}
|
||||
|
||||
public void Enqueue(BotAction action) => Submit(action);
|
||||
|
||||
public void Clear() => _actions.Clear();
|
||||
|
||||
public T? GetHighestPriority<T>() where T : BotAction
|
||||
{
|
||||
T? best = null;
|
||||
foreach (var action in _actions)
|
||||
{
|
||||
if (action is T typed && (best is null || typed.Priority < best.Priority))
|
||||
best = typed;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolve conflicts and return the final action list:
|
||||
/// 1. FlaskActions always pass through
|
||||
/// 2. Get highest-priority MoveAction + CastAction
|
||||
/// 3. Urgent move (priority ≤ 10) → include move, BLOCK cast (flee)
|
||||
/// 4. Normal → include both cast + move
|
||||
/// 5. All other actions pass through
|
||||
/// </summary>
|
||||
public List<BotAction> Resolve()
|
||||
{
|
||||
var resolved = new List<BotAction>();
|
||||
|
||||
// Flasks always pass through
|
||||
foreach (var action in _actions)
|
||||
{
|
||||
if (action is FlaskAction)
|
||||
resolved.Add(action);
|
||||
}
|
||||
|
||||
var bestMove = GetHighestPriority<MoveAction>();
|
||||
var bestCast = GetHighestPriority<CastAction>();
|
||||
|
||||
if (bestMove is not null)
|
||||
{
|
||||
resolved.Add(bestMove);
|
||||
|
||||
// Urgent flee (priority ≤ 10) blocks casting
|
||||
if (bestMove.Priority > 10 && bestCast is not null)
|
||||
resolved.Add(bestCast);
|
||||
}
|
||||
else if (bestCast is not null)
|
||||
{
|
||||
resolved.Add(bestCast);
|
||||
}
|
||||
|
||||
// Pass through everything else (Key, Click, Chat, Wait) except types already handled
|
||||
foreach (var action in _actions)
|
||||
{
|
||||
if (action is MoveAction or CastAction or FlaskAction)
|
||||
continue;
|
||||
resolved.Add(action);
|
||||
}
|
||||
|
||||
Clear();
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
26
src/Roboto.Core/Actions.cs
Normal file
26
src/Roboto.Core/Actions.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace Roboto.Core;
|
||||
|
||||
public enum ClickType { Left, Right }
|
||||
public enum KeyActionType { Press, Down, Up }
|
||||
|
||||
public abstract record BotAction(int Priority);
|
||||
|
||||
public record MoveAction(int Priority, Vector2 Direction) : BotAction(Priority)
|
||||
{
|
||||
/// <summary>Normalized movement direction in world space.</summary>
|
||||
public Vector2 Direction { get; init; } = Direction;
|
||||
}
|
||||
|
||||
public record ClickAction(int Priority, Vector2 ScreenPosition, ClickType Type = ClickType.Left) : BotAction(Priority);
|
||||
|
||||
public record KeyAction(int Priority, ushort ScanCode, KeyActionType Type = KeyActionType.Press) : BotAction(Priority);
|
||||
|
||||
public record CastAction(int Priority, ushort SkillScanCode, Vector2? TargetScreenPos = null) : BotAction(Priority);
|
||||
|
||||
public record FlaskAction(int Priority, ushort FlaskScanCode) : BotAction(Priority);
|
||||
|
||||
public record ChatAction(int Priority, string Message) : BotAction(Priority);
|
||||
|
||||
public record WaitAction(int Priority, int DurationMs) : BotAction(Priority);
|
||||
40
src/Roboto.Core/BotConfig.cs
Normal file
40
src/Roboto.Core/BotConfig.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public class BotConfig
|
||||
{
|
||||
// Tick rates
|
||||
public int LogicTickRateHz { get; set; } = 60;
|
||||
public int MemoryPollRateHz { get; set; } = 30;
|
||||
|
||||
// Movement
|
||||
public float SafeDistance { get; set; } = 400f;
|
||||
public float RepulsionWeight { get; set; } = 1.5f;
|
||||
public float WaypointReachedDistance { get; set; } = 80f;
|
||||
|
||||
// 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;
|
||||
|
||||
// Humanization
|
||||
public int MinReactionDelayMs { get; set; } = 50;
|
||||
public int MaxReactionDelayMs { get; set; } = 150;
|
||||
public float ClickJitterRadius { get; set; } = 3f;
|
||||
public float TimingNoiseStdDev { get; set; } = 0.15f;
|
||||
public int MaxApm { get; set; } = 250;
|
||||
|
||||
// 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
|
||||
}
|
||||
9
src/Roboto.Core/Buff.cs
Normal file
9
src/Roboto.Core/Buff.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public record Buff
|
||||
{
|
||||
public string? Name { get; init; }
|
||||
public float DurationRemaining { get; init; }
|
||||
public int Charges { get; init; }
|
||||
public bool IsDebuff { get; init; }
|
||||
}
|
||||
42
src/Roboto.Core/EntitySnapshot.cs
Normal file
42
src/Roboto.Core/EntitySnapshot.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace Roboto.Core;
|
||||
|
||||
public enum EntityCategory
|
||||
{
|
||||
Unknown,
|
||||
Player,
|
||||
Monster,
|
||||
Npc,
|
||||
WorldItem,
|
||||
Chest,
|
||||
Portal,
|
||||
AreaTransition,
|
||||
Effect,
|
||||
Terrain,
|
||||
MiscObject,
|
||||
}
|
||||
|
||||
public enum MonsterThreatLevel
|
||||
{
|
||||
None,
|
||||
Normal,
|
||||
Magic,
|
||||
Rare,
|
||||
Unique,
|
||||
}
|
||||
|
||||
public record EntitySnapshot
|
||||
{
|
||||
public uint Id { get; init; }
|
||||
public string? Path { get; init; }
|
||||
public EntityCategory Category { get; init; }
|
||||
public MonsterThreatLevel ThreatLevel { get; init; }
|
||||
public Vector2 Position { get; init; }
|
||||
public float DistanceToPlayer { get; init; }
|
||||
public bool IsAlive { get; init; }
|
||||
public int LifeCurrent { get; init; }
|
||||
public int LifeTotal { get; init; }
|
||||
public bool IsTargetable { get; init; }
|
||||
public HashSet<string>? Components { get; init; }
|
||||
}
|
||||
20
src/Roboto.Core/Enums.cs
Normal file
20
src/Roboto.Core/Enums.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public enum DangerLevel
|
||||
{
|
||||
Safe,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Critical,
|
||||
}
|
||||
|
||||
public static class SystemPriority
|
||||
{
|
||||
public const int Threat = 50;
|
||||
public const int Movement = 100;
|
||||
public const int Navigation = 200;
|
||||
public const int Combat = 300;
|
||||
public const int Resource = 400;
|
||||
public const int Loot = 500;
|
||||
}
|
||||
10
src/Roboto.Core/FlaskState.cs
Normal file
10
src/Roboto.Core/FlaskState.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public record FlaskState
|
||||
{
|
||||
public int SlotIndex { get; init; }
|
||||
public int ChargesCurrent { get; init; }
|
||||
public int ChargesMax { get; init; }
|
||||
public bool IsActive { get; init; }
|
||||
public float CooldownRemaining { get; init; }
|
||||
}
|
||||
28
src/Roboto.Core/GameState.cs
Normal file
28
src/Roboto.Core/GameState.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace Roboto.Core;
|
||||
|
||||
public class GameState
|
||||
{
|
||||
public long TickNumber { get; set; }
|
||||
public float DeltaTime { get; set; }
|
||||
public long TimestampMs { get; set; }
|
||||
|
||||
public PlayerState Player { get; set; } = new();
|
||||
public IReadOnlyList<EntitySnapshot> Entities { get; set; } = [];
|
||||
public IReadOnlyList<EntitySnapshot> HostileMonsters { get; set; } = [];
|
||||
public IReadOnlyList<EntitySnapshot> NearbyLoot { get; set; } = [];
|
||||
public WalkabilitySnapshot? Terrain { get; set; }
|
||||
|
||||
public uint AreaHash { get; set; }
|
||||
public int AreaLevel { get; set; }
|
||||
public bool IsLoading { get; set; }
|
||||
public bool IsEscapeOpen { get; set; }
|
||||
public DangerLevel Danger { get; set; }
|
||||
public Matrix4x4? CameraMatrix { get; set; }
|
||||
|
||||
// Derived (computed once per tick by GameStateEnricher)
|
||||
public ThreatMap Threats { get; set; } = new();
|
||||
public IReadOnlyList<EntitySnapshot> NearestEnemies { get; set; } = [];
|
||||
public IReadOnlyList<GroundEffect> GroundEffects { get; set; } = [];
|
||||
}
|
||||
20
src/Roboto.Core/GroundEffect.cs
Normal file
20
src/Roboto.Core/GroundEffect.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace Roboto.Core;
|
||||
|
||||
public record GroundEffect
|
||||
{
|
||||
public Vector2 Position { get; init; }
|
||||
public float Radius { get; init; }
|
||||
public GroundEffectType Type { get; init; }
|
||||
}
|
||||
|
||||
public enum GroundEffectType
|
||||
{
|
||||
Unknown,
|
||||
Fire,
|
||||
Cold,
|
||||
Lightning,
|
||||
Chaos,
|
||||
Physical,
|
||||
}
|
||||
17
src/Roboto.Core/IInputController.cs
Normal file
17
src/Roboto.Core/IInputController.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public interface IInputController
|
||||
{
|
||||
bool IsInitialized { get; }
|
||||
void KeyDown(ushort scanCode);
|
||||
void KeyUp(ushort scanCode);
|
||||
void KeyPress(ushort scanCode, int holdMs = 50);
|
||||
void MouseMoveTo(int x, int y);
|
||||
void MouseMoveBy(int dx, int dy);
|
||||
void LeftClick(int x, int y);
|
||||
void RightClick(int x, int y);
|
||||
void LeftDown();
|
||||
void LeftUp();
|
||||
void RightDown();
|
||||
void RightUp();
|
||||
}
|
||||
9
src/Roboto.Core/IMemoryProvider.cs
Normal file
9
src/Roboto.Core/IMemoryProvider.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public interface IMemoryProvider
|
||||
{
|
||||
bool IsAttached { get; }
|
||||
bool Attach();
|
||||
void Detach();
|
||||
GameState ReadGameState(GameState? previous);
|
||||
}
|
||||
9
src/Roboto.Core/ISystem.cs
Normal file
9
src/Roboto.Core/ISystem.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public interface ISystem
|
||||
{
|
||||
int Priority { get; }
|
||||
string Name { get; }
|
||||
bool IsEnabled { get; set; }
|
||||
void Update(GameState state, ActionQueue actions);
|
||||
}
|
||||
30
src/Roboto.Core/PlayerState.cs
Normal file
30
src/Roboto.Core/PlayerState.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace Roboto.Core;
|
||||
|
||||
public record PlayerState
|
||||
{
|
||||
public Vector2 Position { get; init; }
|
||||
public float Z { get; init; }
|
||||
public bool HasPosition { get; init; }
|
||||
|
||||
public int LifeCurrent { get; init; }
|
||||
public int LifeTotal { get; init; }
|
||||
public int ManaCurrent { get; init; }
|
||||
public int ManaTotal { get; init; }
|
||||
public int EsCurrent { get; init; }
|
||||
public int EsTotal { get; init; }
|
||||
|
||||
public float LifePercent => LifeTotal > 0 ? (float)LifeCurrent / LifeTotal * 100f : 0f;
|
||||
public float ManaPercent => ManaTotal > 0 ? (float)ManaCurrent / ManaTotal * 100f : 0f;
|
||||
public float EsPercent => EsTotal > 0 ? (float)EsCurrent / EsTotal * 100f : 0f;
|
||||
|
||||
// Flask state (populated by memory when available)
|
||||
public IReadOnlyList<FlaskState> Flasks { get; init; } = [];
|
||||
|
||||
// Active buffs (populated by memory when available)
|
||||
public IReadOnlyList<Buff> Buffs { get; init; } = [];
|
||||
|
||||
// Skill slots (populated by memory when available)
|
||||
public IReadOnlyList<SkillState> Skills { get; init; } = [];
|
||||
}
|
||||
7
src/Roboto.Core/Roboto.Core.csproj
Normal file
7
src/Roboto.Core/Roboto.Core.csproj
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
12
src/Roboto.Core/SkillState.cs
Normal file
12
src/Roboto.Core/SkillState.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public record SkillState
|
||||
{
|
||||
public int SlotIndex { get; init; }
|
||||
public ushort ScanCode { get; init; }
|
||||
public string? Name { get; init; }
|
||||
public int ChargesCurrent { get; init; }
|
||||
public int ChargesMax { get; init; }
|
||||
public float CooldownRemaining { get; init; }
|
||||
public bool CanUse => CooldownRemaining <= 0 && ChargesCurrent > 0;
|
||||
}
|
||||
14
src/Roboto.Core/ThreatMap.cs
Normal file
14
src/Roboto.Core/ThreatMap.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace Roboto.Core;
|
||||
|
||||
public class ThreatMap
|
||||
{
|
||||
public int TotalHostiles { get; init; }
|
||||
public int CloseRange { get; init; } // < 300 units
|
||||
public int MidRange { get; init; } // 300–600
|
||||
public int FarRange { get; init; } // 600–1200
|
||||
public float ClosestDistance { get; init; } = float.MaxValue;
|
||||
public Vector2 ThreatCentroid { get; init; }
|
||||
public bool HasRareOrUnique { get; init; }
|
||||
}
|
||||
15
src/Roboto.Core/WalkabilitySnapshot.cs
Normal file
15
src/Roboto.Core/WalkabilitySnapshot.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
namespace Roboto.Core;
|
||||
|
||||
public record WalkabilitySnapshot
|
||||
{
|
||||
public int Width { get; init; }
|
||||
public int Height { get; init; }
|
||||
public byte[] Data { get; init; } = [];
|
||||
|
||||
public bool IsWalkable(int x, int y)
|
||||
{
|
||||
if (x < 0 || x >= Width || y < 0 || y >= Height)
|
||||
return false;
|
||||
return Data[y * Width + x] != 0;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue