MosswartMassacre/MosswartMassacre/Utils.cs

224 lines
8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 players 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 characters 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) arent 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;
}
}
}