Initial import of IC.Rebuild

This commit is contained in:
2026-10-13 22:55:05 +08:00
commit 16d61f7a5e
87 changed files with 17054 additions and 0 deletions
+64
View File
@@ -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();
}
}