224 lines
8 KiB
C#
224 lines
8 KiB
C#
using System;
|
||
using Decal.Adapter;
|
||
using Decal.Adapter.Wrappers;
|
||
using System.Numerics;
|
||
using Mag.Shared.Constants;
|
||
using System.Linq;
|
||
|
||
namespace MosswartMassacre
|
||
{
|
||
/// <summary>
|
||
/// Misc. helpers shared across the plugin.
|
||
/// Add new utilities here as your project grows.
|
||
/// </summary>
|
||
public static class Utils
|
||
{
|
||
/* ----------------------------------------------------------
|
||
* 1) Direct-memory physics helpers
|
||
* -------------------------------------------------------- */
|
||
|
||
/// <summary>
|
||
/// Return the player’s raw world position by reading the
|
||
/// physics-object pointer (same offsets UB uses: +0x84/88/8C).
|
||
/// </summary>
|
||
public static unsafe Vector3 GetPlayerPosition()
|
||
{
|
||
try
|
||
{
|
||
int id = CoreManager.Current.CharacterFilter.Id;
|
||
if (!CoreManager.Current.Actions.IsValidObject(id))
|
||
return new Vector3();
|
||
|
||
byte* p = (byte*)CoreManager.Current.Actions.Underlying.GetPhysicsObjectPtr(id);
|
||
return new Vector3(
|
||
*(float*)(p + 0x84), // X
|
||
*(float*)(p + 0x88), // Y
|
||
*(float*)(p + 0x8C)); // Z
|
||
}
|
||
catch
|
||
{
|
||
return new Vector3();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Return any WorldObject's raw world position by reading the
|
||
/// physics-object pointer (same offsets: +0x84/88/8C).
|
||
/// </summary>
|
||
public static unsafe Vector3 GetWorldObjectPosition(int objectId)
|
||
{
|
||
try
|
||
{
|
||
if (!CoreManager.Current.Actions.IsValidObject(objectId))
|
||
return new Vector3();
|
||
|
||
byte* p = (byte*)CoreManager.Current.Actions.Underlying.GetPhysicsObjectPtr(objectId);
|
||
return new Vector3(
|
||
*(float*)(p + 0x84), // X
|
||
*(float*)(p + 0x88), // Y
|
||
*(float*)(p + 0x8C)); // Z
|
||
}
|
||
catch
|
||
{
|
||
return new Vector3();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Convenience: returns the current landcell (upper 16 bits of landblock).
|
||
/// </summary>
|
||
public static uint GetPlayerLandcell() =>
|
||
(uint)CoreManager.Current.Actions.Landcell;
|
||
|
||
/* ----------------------------------------------------------
|
||
* 2) High-level wrappers around Geometry / Coordinates
|
||
* -------------------------------------------------------- */
|
||
|
||
/// <summary>
|
||
/// Get AC-style coordinates (EW/NS) for the player in one call.
|
||
/// </summary>
|
||
public static Coordinates GetPlayerCoordinates()
|
||
{
|
||
Vector3 pos = GetPlayerPosition();
|
||
uint landcell = GetPlayerLandcell();
|
||
|
||
double ew = Geometry.LandblockToEW(landcell, pos.X);
|
||
double ns = Geometry.LandblockToNS(landcell, pos.Y);
|
||
|
||
return new Coordinates(ew, ns, pos.Z);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get AC-style coordinates (EW/NS/Z) for any WorldObject.
|
||
/// </summary>
|
||
public static Coordinates GetWorldObjectCoordinates(WorldObject wo)
|
||
{
|
||
if (wo == null) return new Coordinates();
|
||
|
||
Vector3 pos = GetWorldObjectPosition(wo.Id);
|
||
|
||
// Get landcell from the object's coordinates
|
||
var coordsObj = wo.Coordinates();
|
||
if (coordsObj == null) return new Coordinates();
|
||
|
||
// Convert DECAL coords to our Coordinates with Z
|
||
double ew = coordsObj.EastWest;
|
||
double ns = coordsObj.NorthSouth;
|
||
|
||
return new Coordinates(ew, ns, pos.Z);
|
||
}
|
||
|
||
/* ----------------------------------------------------------
|
||
* 3) Generic math helpers you may want later
|
||
* -------------------------------------------------------- */
|
||
|
||
public static double Clamp(double value, double min, double max) =>
|
||
value < min ? min : (value > max ? max : value);
|
||
|
||
public static double DegToRad(double deg) => deg * Math.PI / 180.0;
|
||
public static double RadToDeg(double rad) => rad * 180.0 / Math.PI;
|
||
|
||
/* ----------------------------------------------------------
|
||
* 4) Generic item property access
|
||
* -------------------------------------------------------- */
|
||
|
||
/// <summary>
|
||
/// Find a WorldObject item by name in inventory
|
||
/// </summary>
|
||
/// <param name="itemName">Name of the item to find</param>
|
||
/// <returns>WorldObject or null if not found</returns>
|
||
public static WorldObject FindItemByName(string itemName)
|
||
{
|
||
try
|
||
{
|
||
//var worldFilter = CoreManager.Current.WorldFilter;
|
||
//var playerInv = CoreManager.Current.CharacterFilter.Id;
|
||
|
||
// Search inventory
|
||
|
||
foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
|
||
{
|
||
if (string.Equals(wo.Name, itemName, StringComparison.OrdinalIgnoreCase))
|
||
return wo;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
catch
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get the stack size/quantity of a specific item by name
|
||
/// </summary>
|
||
/// <param name="itemName">Name of the item to find</param>
|
||
/// <returns>Stack size or 0 if not found</returns>
|
||
/// <summary>
|
||
/// Return the total quantity of an item in the character’s inventory,
|
||
/// adding up every stack that shares <paramref name="itemName"/>.
|
||
/// </summary>
|
||
public static int GetItemStackSize(string itemName)
|
||
{
|
||
try
|
||
{
|
||
// 1. Pull every WorldObject in bags + containers
|
||
var inv = CoreManager.Current.WorldFilter.GetInventory();
|
||
|
||
// 2. Keep only those whose display name matches (case-insensitive)
|
||
// 3. For each one, use StackCount if it exists, otherwise treat as 1
|
||
return inv.Where(wo =>
|
||
string.Equals(wo.Name, itemName,
|
||
StringComparison.OrdinalIgnoreCase))
|
||
.Sum(wo =>
|
||
{
|
||
// Some items (weapons, armor) aren’t stackable;
|
||
// Values(LongValueKey.StackCount) throws if the key is absent.
|
||
try
|
||
{
|
||
return wo.Values(LongValueKey.StackCount);
|
||
}
|
||
catch
|
||
{
|
||
return 1; // non-stackable item = quantity 1
|
||
}
|
||
});
|
||
}
|
||
catch
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Get the icon ID of a specific item by name
|
||
/// </summary>
|
||
/// <param name="itemName">Name of the item to find</param>
|
||
/// <returns>Icon ID or 0 if not found</returns>
|
||
public static int GetItemIcon(string itemName)
|
||
{
|
||
try
|
||
{
|
||
var item = FindItemByName(itemName);
|
||
return item?.Icon ?? 0;
|
||
}
|
||
catch
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get the display icon ID (with 0x6000000 offset) for an item by name
|
||
/// </summary>
|
||
/// <param name="itemName">Name of the item to find</param>
|
||
/// <returns>Display icon ID or 0x6002D14 (default icon) if not found</returns>
|
||
public static int GetItemDisplayIcon(string itemName)
|
||
{
|
||
int rawIcon = GetItemIcon(itemName);
|
||
return rawIcon != 0 ? rawIcon + 0x6000000 : 0x6002D14;
|
||
}
|
||
}
|
||
}
|