Initial import of IC.Rebuild
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using ICRebuild.Core;
|
||||
|
||||
namespace ICRebuild.Runtime;
|
||||
|
||||
public sealed unsafe class CoordinateTools(GameContext game, ModuleManager modules)
|
||||
{
|
||||
private delegate void Scale(nint actor, float scale);
|
||||
private Scale? scale;
|
||||
private readonly Dictionary<ulong,(nint Address,uint Territory,float Original,float Applied)> resized=[];
|
||||
public void Register()
|
||||
{
|
||||
modules.Register(27, _ => { }); modules.Register(28, _ => { });
|
||||
modules.Register(29, scope =>
|
||||
{
|
||||
scale = Marshal.GetDelegateForFunctionPointer<Scale>(game.Scan("F3 0F 11 89 ?? ?? ?? ?? E9 ?? ?? ?? ?? CC CC CC CC CC CC CC CC CC CC CC CC"));
|
||||
scope.Defer(() => { try { RestoreAll(); } finally { scale=null; } });
|
||||
});
|
||||
}
|
||||
private GameObject* Player(int feature)
|
||||
{
|
||||
if (!modules.IsEnabled(feature)) throw new InvalidOperationException($"先启用模块 {feature}");
|
||||
if (!game.Ready) throw new InvalidOperationException("角色尚未就绪");
|
||||
return (GameObject*)game.LocalAddress;
|
||||
}
|
||||
public void Position(Vector3 position)
|
||||
{
|
||||
if (!float.IsFinite(position.X) || !float.IsFinite(position.Y) || !float.IsFinite(position.Z) ||
|
||||
Math.Abs(position.X)>100000 || Math.Abs(position.Y)>100000 || Math.Abs(position.Z)>100000)
|
||||
throw new ArgumentException("坐标必须是 ±100000 范围内的有限数");
|
||||
Player(27)->SetPosition(position.X,position.Y,position.Z);
|
||||
}
|
||||
public void Facing(float rotation)
|
||||
{
|
||||
if (!float.IsFinite(rotation)) throw new ArgumentException("面向必须是有限数");
|
||||
Player(28)->SetRotation(MathF.IEEERemainder(rotation,2*MathF.PI));
|
||||
}
|
||||
public void SetScale(float value)
|
||||
{
|
||||
if (!modules.IsEnabled(29) || scale == null) throw new InvalidOperationException("先启用模块 29");
|
||||
if (!game.Ready || game.Targets.Target == null) throw new InvalidOperationException("请选中有效目标");
|
||||
if (!float.IsFinite(value) || value < 0.1f || value > 5) throw new ArgumentException("缩放范围为 0.1—5");
|
||||
var target=game.Targets.Target;
|
||||
var obj=(GameObject*)target.Address;
|
||||
var original=obj->Scale;
|
||||
if(!float.IsFinite(original) || original<=0) throw new InvalidOperationException("目标原始缩放无效");
|
||||
if(resized.TryGetValue(target.GameObjectId,out var old) && old.Address==target.Address && old.Territory==game.Client.TerritoryType)
|
||||
original=old.Original;
|
||||
if(resized.Count>=256 && !resized.ContainsKey(target.GameObjectId)) throw new InvalidOperationException("先恢复已有缩放对象");
|
||||
scale(target.Address,value);
|
||||
resized[target.GameObjectId]=(target.Address,game.Client.TerritoryType,original,value);
|
||||
}
|
||||
public void RestoreAll()
|
||||
{
|
||||
if(scale!=null && game.Ready)
|
||||
foreach(var obj in game.Objects)
|
||||
if(resized.TryGetValue(obj.GameObjectId,out var old) && old.Address==obj.Address && old.Territory==game.Client.TerritoryType &&
|
||||
Math.Abs(((GameObject*)obj.Address)->Scale-old.Applied)<0.0001f)
|
||||
scale(obj.Address,old.Original);
|
||||
resized.Clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user