From 32ff609ee5144069f04a0c3ba06c9d7ba57c7980 Mon Sep 17 00:00:00 2001 From: "E. S." <39125931+ClonkAndre@users.noreply.github.com> Date: Thu, 10 Oct 2024 20:57:41 +0200 Subject: [PATCH 1/5] Added vs project --- PackAndCarry/PackAndCarry.sln | 25 + PackAndCarry/PackAndCarry/Classes/Logging.cs | 29 + .../PackAndCarry/Classes/ModSettings.cs | 28 + PackAndCarry/PackAndCarry/Main.cs | 1008 +++++++++++++++++ PackAndCarry/PackAndCarry/PackAndCarry.csproj | 63 ++ .../PackAndCarry/Properties/AssemblyInfo.cs | 36 + PackAndCarry/PackAndCarry/packages.config | 5 + 7 files changed, 1194 insertions(+) create mode 100644 PackAndCarry/PackAndCarry.sln create mode 100644 PackAndCarry/PackAndCarry/Classes/Logging.cs create mode 100644 PackAndCarry/PackAndCarry/Classes/ModSettings.cs create mode 100644 PackAndCarry/PackAndCarry/Main.cs create mode 100644 PackAndCarry/PackAndCarry/PackAndCarry.csproj create mode 100644 PackAndCarry/PackAndCarry/Properties/AssemblyInfo.cs create mode 100644 PackAndCarry/PackAndCarry/packages.config diff --git a/PackAndCarry/PackAndCarry.sln b/PackAndCarry/PackAndCarry.sln new file mode 100644 index 0000000..ae1482d --- /dev/null +++ b/PackAndCarry/PackAndCarry.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34728.123 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackAndCarry", "PackAndCarry\PackAndCarry.csproj", "{9160C51B-B899-4C30-87DD-6DC7E04E079F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9160C51B-B899-4C30-87DD-6DC7E04E079F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9160C51B-B899-4C30-87DD-6DC7E04E079F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9160C51B-B899-4C30-87DD-6DC7E04E079F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9160C51B-B899-4C30-87DD-6DC7E04E079F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2CC860CA-D258-4996-99BF-A79FBEE5BB68} + EndGlobalSection +EndGlobal diff --git a/PackAndCarry/PackAndCarry/Classes/Logging.cs b/PackAndCarry/PackAndCarry/Classes/Logging.cs new file mode 100644 index 0000000..0fc8ee9 --- /dev/null +++ b/PackAndCarry/PackAndCarry/Classes/Logging.cs @@ -0,0 +1,29 @@ +using IVSDKDotNet; + +namespace PackAndCarry.Classes +{ + internal class Logging + { + + public static void Log(string str, params object[] args) + { + IVGame.Console.Print(string.Format("[PackAndCarry] {0}", string.Format(str, args))); + } + public static void LogWarning(string str, params object[] args) + { + IVGame.Console.PrintWarning(string.Format("[PackAndCarry] {0}", string.Format(str, args))); + } + public static void LogError(string str, params object[] args) + { + IVGame.Console.PrintError(string.Format("[PackAndCarry] {0}", string.Format(str, args))); + } + + public static void LogDebug(string str, params object[] args) + { +#if DEBUG + IVGame.Console.Print(string.Format("[PackAndCarry] [DEBUG] {0}", string.Format(str, args))); +#endif + } + + } +} diff --git a/PackAndCarry/PackAndCarry/Classes/ModSettings.cs b/PackAndCarry/PackAndCarry/Classes/ModSettings.cs new file mode 100644 index 0000000..0dfcb8f --- /dev/null +++ b/PackAndCarry/PackAndCarry/Classes/ModSettings.cs @@ -0,0 +1,28 @@ +using System.Numerics; + +using IVSDKDotNet; + +namespace PackAndCarry.Classes +{ + internal class ModSettings + { + + #region Variables + // General + public static bool DisableInMP; + + // Inventory + public static int DefaultCapacity; + #endregion + + public static void Load(SettingsFile settings) + { + // General + DisableInMP = settings.GetBoolean("General", "DisableInMP", false); + + // Inventory + DefaultCapacity = settings.GetInteger("Inventory", "DefaultCapacity", 10); + } + + } +} diff --git a/PackAndCarry/PackAndCarry/Main.cs b/PackAndCarry/PackAndCarry/Main.cs new file mode 100644 index 0000000..a850d9e --- /dev/null +++ b/PackAndCarry/PackAndCarry/Main.cs @@ -0,0 +1,1008 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Numerics; +using System.Windows.Forms; +using System.Runtime.InteropServices; + +using CCL.GTAIV; + +using CustomInventoryIV; +using CustomInventoryIV.Base; +using CustomInventoryIV.Inventories; + +using PackAndCarry.Classes; + +using IVSDKDotNet; +using IVSDKDotNet.Enums; +using static IVSDKDotNet.Native.Natives; + +namespace PackAndCarry +{ + public class Main : Script + { + + #region DllImports + [DllImport("user32.dll")] + private static extern bool SetCursorPos(int X, int Y); + #endregion + + #region Variables + // Lists + private InventoryPool inventoryPool; + private Dictionary loadedWeaponTextures; + + // Inventory stuff + private BasicInventory playerInventory; + private Stopwatch inventoryKeyWatch; + private bool wasInventoryOpenedViaController; + public int InventoryOpenTimeInMS = 150; + + // Pickup stuff + private bool blockPlayerAbilityToCollectPickup; + private Vector3 lastPickupPosition; + + // Other + private int playerPedHandle; + private int lastPlayerWeapon; + private uint lastLoadedEpisode; + + private float timeScaleInterpolationValue; + private bool wasCursorPosSet; + #endregion + + #region Methods + private void OpenInventory(bool wasOpenedUsingController) + { + playerInventory.IsVisible = true; + + if (wasOpenedUsingController) + wasInventoryOpenedViaController = true; + } + private void CloseInventory() + { + playerInventory.IsVisible = false; + wasInventoryOpenedViaController = false; + } + + private void HandleTimescaleInterpolation() + { + if (IVNetwork.IsNetworkGameRunning()) + return; + + if (playerInventory.IsVisible) + { + timeScaleInterpolationValue = timeScaleInterpolationValue + 0.02f; + + if (timeScaleInterpolationValue > 1.0f) + timeScaleInterpolationValue = 1.0f; + } + else + { + timeScaleInterpolationValue = timeScaleInterpolationValue - 0.03f; + + if (timeScaleInterpolationValue < 0.0f) + timeScaleInterpolationValue = 0.0f; + } + + IVTimer.TimeScale = Lerp(1.0f, 0.25f, timeScaleInterpolationValue); + } + private void HandlePreventPlayerToCollectPickup() + { + if (!blockPlayerAbilityToCollectPickup) + return; + + // Get the player position + GET_CHAR_COORDINATES(playerPedHandle, out Vector3 pos); + + if (Vector3.Distance(pos, lastPickupPosition) > 2.5f) + { + DISABLE_LOCAL_PLAYER_PICKUPS(false); + blockPlayerAbilityToCollectPickup = false; + } + else + { + DISABLE_LOCAL_PLAYER_PICKUPS(true); + } + } + + private void DropItem(BasicInventory inventory, BasicInventoryItem item, float range = 0.0F) + { + if (inventory == null) + return; + if (item == null) + return; + + int weaponType = Convert.ToInt32(item.Tags["WeaponType"]); + + GET_AMMO_IN_CHAR_WEAPON(playerPedHandle, weaponType, out int ammo); + + REMOVE_WEAPON_FROM_CHAR(playerPedHandle, weaponType); + + GET_CHAR_COORDINATES(playerPedHandle, out Vector3 pos); + + if (range != 0.0F) + pos = pos.Around(range); + + // Disable ability for local player to any pickups + DISABLE_LOCAL_PLAYER_PICKUPS(true); + blockPlayerAbilityToCollectPickup = true; + lastPickupPosition = pos; + + // Creates a weapon pickup at the players location + CreateWeaponPickupAtPosition(pos, weaponType, ammo); + + // Removes the item out of the inventory + inventory.RemoveItem(item); + } + + private void LoadTextures() + { + if (loadedWeaponTextures.Count != 0) + { + // Destroy loaded textures if changing episode + if (lastLoadedEpisode != IVGame.CurrentEpisodeMenu) + { + foreach (KeyValuePair item in loadedWeaponTextures) + { + IntPtr texture = item.Value.GetTexture(); + ImGuiIV.ReleaseTexture(ref texture); + } + loadedWeaponTextures.Clear(); + } + } + + // Set episode + lastLoadedEpisode = IVGame.CurrentEpisodeMenu; + + string path = string.Format("{0}\\Icons\\Weapons\\{1}", ScriptResourceFolder, lastLoadedEpisode); + + // Create textures for the current episode + string[] files = Directory.GetFiles(path, "*.dds", SearchOption.TopDirectoryOnly); + for (int i = 0; i < files.Length; i++) + { + string file = files[i]; + string fileName = Path.GetFileName(file); + + if (int.TryParse(fileName.Split('.')[0], out int result)) + { + // Create texture + if (ImGuiIV.CreateTextureFromFile(string.Format("{0}\\{1}", path, fileName), out IntPtr txtPtr, out int w, out int h, out eResult r)) + { + loadedWeaponTextures.Add(result, new CITexture(txtPtr, new Size(w, h))); + } + else + { + IVGame.Console.PrintError(string.Format("Failed to create texture {0}! Result: {1}", fileName, r)); + } + } + } + } + #endregion + + #region Functions + private static float Lerp(float a, float b, float t) + { + // Clamp t between 0 and 1 + t = Math.Max(0.0f, Math.Min(1.0f, t)); + + return a + (b - a) * t; + } + + private int CreateWeaponPickupAtPosition(Vector3 pos, int weaponType, int ammo) + { + GET_WEAPONTYPE_MODEL(weaponType, out uint model); + + Vector3 spawnPos = NativeWorld.GetGroundPosition(pos) + new Vector3(0f, 0f, 0.05f); + CREATE_PICKUP_ROTATE(model, (uint)ePickupType.PICKUP_TYPE_WEAPON, (uint)ammo, spawnPos, new Vector3(90f, 0f, GENERATE_RANDOM_FLOAT_IN_RANGE(0f, 90f)), out int pickup); + + // DOES_PICKUP_EXIST + // REMOVE_PICKUP + + return pickup; + } + + private BasicInventory FindInventory(Guid id) + { + // Find target inventory + InventoryBase inventoryBase = inventoryPool.Get(id); + + if (inventoryBase == null) + return null; + + // Convert inventory base to basic inventory + return (BasicInventory)inventoryBase; + } + #endregion + + #region Events + private void BasicInventory_OnItemDraggedOut(BasicInventory sender, BasicInventoryItem item, int itemIndex) + { + if (sender.Name == "TestInventory") + { + DropItem(sender, item); + } + } + private void BasicInventory_OnItemDraggedToNewSlot(BasicInventory sender, BasicInventoryItem item, int oldIndex, int newIndex) + { + IVGame.Console.PrintWarning(string.Format("Item {0} was dragged from slot {1} to {2}", item.TopLeftText, oldIndex, newIndex)); + } + private void BasicInventory_OnPopupItemClick(BasicInventory sender, BasicInventoryItem item, string popupItemName) + { + if (sender.Name == "TestInventory") + { + if (popupItemName == "Drop") + { + DropItem(sender, item); + } + } + } + private void BasicInventory_OnItemClick(BasicInventory sender, BasicInventoryItem item, int itemIndex) + { + if (item.Tags.ContainsKey("IS_GAME_WEAPON")) + { + int weaponType = Convert.ToInt32(item.Tags["WeaponType"]); + lastPlayerWeapon = weaponType; + SET_CURRENT_CHAR_WEAPON(playerPedHandle, weaponType, false); + } + + if (wasInventoryOpenedViaController) + CloseInventory(); + + //if (itemIndex == 0) + //{ + // sender.Resize(12); + //} + //else if (itemIndex == 1) + //{ + // List leftBehindItems = sender.Resize(8); + + // if (leftBehindItems != null) + // { + // IVGame.Console.PrintError(string.Format("There are {0} left behind items:", leftBehindItems.Count)); + // for (int i = 0; i < leftBehindItems.Count; i++) + // { + // BasicInventoryItem leftBehindItem = leftBehindItems[i]; + // IVGame.Console.PrintWarning(leftBehindItem.TopLeftText); + // } + // } + // else + // { + // IVGame.Console.PrintError("There where no left behind items."); + // } + //} + } + private void BasicInventory_OnInventoryResized(BasicInventory target, List leftBehindItems) + { + if (leftBehindItems != null) + { + for (int i = 0; i < leftBehindItems.Count; i++) + { + BasicInventoryItem item = leftBehindItems[i]; + + if (item.Tags.ContainsKey("IS_GAME_WEAPON")) + DropItem(target, item, i * 0.15f); + } + } + } + #endregion + + #region Constructor + public Main() + { + // Lists + loadedWeaponTextures = new Dictionary(32); + + // Other + inventoryKeyWatch = new Stopwatch(); + + // IV-SDK .NET stuff + Uninitialize += Main_Uninitialize; + Initialized += Main_Initialized; + GameLoad += Main_GameLoad; + OnImGuiRendering += Main_OnImGuiRendering; + ScriptCommandReceived += Main_ScriptCommandReceived; + Tick += Main_Tick; + } + #endregion + + private void Main_Uninitialize(object sender, EventArgs e) + { + if (loadedWeaponTextures != null) + { + loadedWeaponTextures.Clear(); + loadedWeaponTextures = null; + } + if (inventoryPool != null) + { + inventoryPool.Clear(); + inventoryPool = null; + } + if (playerInventory != null) + { + playerInventory.Clear(); + playerInventory = null; + } + } + private void Main_Initialized(object sender, EventArgs e) + { + ModSettings.Load(Settings); + + // Create inventory pool + inventoryPool = new InventoryPool(); + + // Create the inventory + playerInventory = new BasicInventory("PlayerInventory", ModSettings.DefaultCapacity); + playerInventory.OnItemDraggedOut += BasicInventory_OnItemDraggedOut; + playerInventory.OnPopupItemClick += BasicInventory_OnPopupItemClick; + playerInventory.OnItemClick += BasicInventory_OnItemClick; + playerInventory.OnInventoryResized += BasicInventory_OnInventoryResized; + playerInventory.OnItemDraggedToNewSlot += BasicInventory_OnItemDraggedToNewSlot; + playerInventory.ItemSize = new Vector2(128f, 100f); + + inventoryPool.Add(playerInventory); + } + + private void Main_GameLoad(object sender, EventArgs e) + { + if (playerInventory != null) + playerInventory.Clear(); + + LoadTextures(); + } + + private void Main_OnImGuiRendering(IntPtr devicePtr, ImGuiIV_DrawingContext ctx) + { + if (inventoryPool != null) + inventoryPool.ProcessDrawing(ctx); + } + + private object Main_ScriptCommandReceived(Script fromScript, object[] args, string command) + { + try + { + switch (command) + { + // Inventory + case "GET_PLAYER_INVENTORY_ID": + { + if (playerInventory != null) + return playerInventory.ID; + + return Guid.Empty; + } + + case "GET_AMOUNT_OF_FREE_SLOTS_IN_INVENTORY": + { + Guid inventoryId = (Guid)args[0]; + + // Find target inventory + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return 0; + + return inventory.GetAmountOfFreeSlots(); + } + + // Item + case "ADD_NEW_ITEM_TO_INVENTORY": + { + Guid inventoryId = (Guid)args[0]; + + // Find target inventory + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return Guid.Empty; + + // Check if there are any free slots in the inventory for the new item + if (inventory.GetAmountOfFreeSlots() == 0) + return Guid.Empty; + + // Get item details + uint hash = Convert.ToUInt32(args[1]); + string buttonText = Convert.ToString(args[2]); + string topLeftText = Convert.ToString(args[3]); + string topRightText = Convert.ToString(args[4]); + string bottomLeftText = Convert.ToString(args[5]); + string bottomRightText = Convert.ToString(args[6]); + + // Create new item + BasicInventoryItem item = new BasicInventoryItem(hash); + item.ButtonText = buttonText; + item.TopLeftText = topLeftText; + item.TopRightText = topRightText; + item.BottomLeftText = bottomLeftText; + item.BottomRightText = bottomRightText; + + // Add item to inventory + if (!inventory.AddItem(item)) + { + item = null; + return Guid.Empty; + } + + item.Tags.Add("IS_CUSTOM_ITEM", null); + item.Tags.Add("OWNER_SCRIPT_ID", fromScript.ID); // TODO: NEED TO ADD A WAY FOR SCRIPTS TO CHANGE THEIR ID + + return item.ID; + } + case "REMOVE_ITEM_FROM_INVENTORY": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Try remove target item + return inventory.RemoveItem((Guid)args[1]); + } + case "DOES_ITEM_EXISTS_IN_INVENTORY": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Check if item exists in inventory + return inventory.ContainsItem((Guid)args[1]); + } + + case "ADD_TAG_TO_ITEM": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Check if tag is already present + string tag = Convert.ToString(args[2]); + + if (item.Tags.ContainsKey(tag)) + return false; + + // Add tag to item + item.Tags.Add(tag, args[3]); + + return true; + } + case "REMOVE_TAG_FROM_ITEM": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Check if tag is present + string tag = Convert.ToString(args[2]); + + if (!item.Tags.ContainsKey(tag)) + return false; + + // Try remove tag from item + return item.Tags.Remove(tag); + } + case "DOES_ITEM_HAVE_TAG": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Check if tag is present + return item.Tags.ContainsKey(Convert.ToString(args[2])); + } + + case "ADD_POPUP_ITEM_TO_ITEM": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Check if popup item is already present + string popupItem = Convert.ToString(args[2]); + + if (item.PopupMenuItems.Contains(popupItem)) + return false; + + // Add popup item to item + item.PopupMenuItems.Add(popupItem); + + return true; + } + case "REMOVE_POPUP_ITEM_FROM_ITEM": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Check if popup item is already present + string popupItem = Convert.ToString(args[2]); + + if (!item.PopupMenuItems.Contains(popupItem)) + return false; + + // Try remove popup item from item + return item.PopupMenuItems.Remove(popupItem); + } + case "DOES_ITEM_HAVE_POPUP_ITEM": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Check if popup item is present + return item.PopupMenuItems.Contains(Convert.ToString(args[2])); + } + + case "ADD_ICON_TO_ITEM": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Get icon details + IntPtr texture = (IntPtr)args[2]; + int width = Convert.ToInt32(args[3]); + int height = Convert.ToInt32(args[4]); + + // Set icon + item.Icon = new CITexture(texture, new Size(width, height)); + + return true; + } + case "REMOVE_ICON_FROM_ITEM": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Set icon + item.Icon = null; + + return true; + } + + case "SET_ITEM_NOT_REMOVED_FROM_INVENTORY_ON_MOD_UNLOAD": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + bool set = Convert.ToBoolean(args[2]); + + // Check if tag is already present + string tag = "DO_NOT_REMOVE_ITEM_WHEN_MOD_THAT_ADDED_IT_UNLOADS"; + + if (set) + { + if (!item.Tags.ContainsKey(tag)) + item.Tags.Add(tag, null); + } + else + { + if (item.Tags.ContainsKey(tag)) + item.Tags.Remove(tag); + } + + return true; + } + case "SET_ITEM_TOOLTIP": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Set tooltip + item.ButtonTooltip = Convert.ToString(args[2]); + + return true; + } + + // Events (TODO) + case "SUBSCRIBE_TO_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Check if tag is already present + string tag = "WANTS_ON_POPUP_ITEM_CLICK_EVENTS"; + + if (item.Tags.ContainsKey(tag)) + return false; + + // Add tag to item + item.Tags.Add(tag, fromScript.ID); // TODO: NEED TO ADD A WAY FOR SCRIPTS TO CHANGE THEIR ID + + return true; + } + case "UNSUBSCRIBE_FROM_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM": + { + // Find target inventory + Guid inventoryId = (Guid)args[0]; + + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + Guid itemId = (Guid)args[1]; + + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Check if tag is present + string tag = "WANTS_ON_POPUP_ITEM_CLICK_EVENTS"; + + if (!item.Tags.ContainsKey(tag)) + return false; + + // Remove tag from item + item.Tags.Remove(tag); + + return true; + } + + } + } + catch (Exception ex) + { + string senderScriptName = fromScript != null ? fromScript.GetName() : "UNKNOWN"; + string targetCommand = command != null ? command : "UNKNOWN"; + + Logging.LogDebug("Command '{0}' which was sent by script '{1}' caused an exception! Details: {2}", targetCommand, senderScriptName, ex); + } + + return null; + } + + private void Main_Tick(object sender, EventArgs e) + { + if (ModSettings.DisableInMP && IVNetwork.IsNetworkGameRunning()) + return; + + // Get player stuff + IVPed playerPed = IVPed.FromUIntPtr(IVPlayerInfo.FindThePlayerPed()); + playerPedHandle = playerPed.GetHandle(); + + // Load weapon textures if not loaded yet + if (loadedWeaponTextures.Count == 0) + LoadTextures(); + + // Handle inventory open/closing and quick weapon switch to last weapon or fist + bool wantsToOpenViaController = NativeControls.IsUsingJoypad() && NativeControls.IsControllerButtonPressed(0, ControllerButton.BUTTON_BUMPER_LEFT); + if (IsKeyPressed(Keys.Tab) || wantsToOpenViaController) + { + if (inventoryKeyWatch.IsRunning) + { + if (inventoryKeyWatch.ElapsedMilliseconds > InventoryOpenTimeInMS) + { + if (!playerInventory.IsVisible) + { + if (!wasCursorPosSet) + { + GET_SCREEN_RESOLUTION(out int x, out int y); + SetCursorPos(x / 2, y / 2); + wasCursorPosSet = true; + } + + OpenInventory(wantsToOpenViaController); + } + } + } + else + { + inventoryKeyWatch.Start(); + } + } + else + { + if (inventoryKeyWatch.IsRunning) + { + if (inventoryKeyWatch.ElapsedMilliseconds < InventoryOpenTimeInMS) + { + // Switch to last weapon or fist + GET_CURRENT_CHAR_WEAPON(playerPedHandle, out int currentWeapon); + + if ((eWeaponType)currentWeapon == eWeaponType.WEAPON_UNARMED) + { + if (HAS_CHAR_GOT_WEAPON(playerPedHandle, lastPlayerWeapon)) + SET_CURRENT_CHAR_WEAPON(playerPedHandle, lastPlayerWeapon, false); + } + else + { + lastPlayerWeapon = currentWeapon; + SET_CURRENT_CHAR_WEAPON(playerPedHandle, (int)eWeaponType.WEAPON_UNARMED, false); + } + } + + inventoryKeyWatch.Reset(); + + if (!wasInventoryOpenedViaController) + playerInventory.IsVisible = false; + } + } + + // Do stuff when inventory is open or not + if (playerInventory.IsVisible) + { + // Do stuff only when inventory was opened via controller + if (wasInventoryOpenedViaController) + { + if (!playerInventory.IsAnyItemFocused) + { + if (ImGuiIV.IsKeyDown(eImGuiKey.ImGuiKey_GamepadFaceRight)) + CloseInventory(); + } + else + { + if (ImGuiIV.IsKeyDown(eImGuiKey.ImGuiKey_GamepadFaceUp)) + { + BasicInventoryItem item = playerInventory.GetFocusedItem(); + + if (item != null) + DropItem(playerInventory, item); + } + } + } + + // Position the inventory at the players head + GET_PED_BONE_POSITION(playerPedHandle, (uint)eBone.BONE_HEAD, Vector3.Zero, out Vector3 headPos); + playerInventory.PositionAtWorldCoordinate(headPos); + } + else + { + // Reset some states + wasCursorPosSet = false; + } + + // Handle some other stuff + HandleTimescaleInterpolation(); + HandlePreventPlayerToCollectPickup(); + + // Update all inventory items + BasicInventoryItem[] items = playerInventory.GetItems(); + for (int i = 0; i < items.Length; i++) + { + BasicInventoryItem item = items[i]; + + // Check if this item is a included weapon + if (!item.Tags.ContainsKey("IS_GAME_WEAPON")) + continue; + + // Get what kind of weapon this item is supposed to be + int weaponType = Convert.ToInt32(item.Tags["WeaponType"]); + + // Check if player still has this weapon + if (!HAS_CHAR_GOT_WEAPON(playerPedHandle, weaponType)) + { + // Remove the item + playerInventory.RemoveItem(item); + continue; + } + + GET_AMMO_IN_CHAR_WEAPON(playerPedHandle, weaponType, out int ammo); + + // Check if ammo in weapon is not zero + if (ammo == 0) + { + // Remove the item + playerInventory.RemoveItem(item); + continue; + } + + // Update the item + switch ((eWeaponType)weaponType) + { + case eWeaponType.WEAPON_BASEBALLBAT: + case eWeaponType.WEAPON_KNIFE: + case eWeaponType.WEAPON_POOLCUE: + item.BottomLeftText = "1x"; + break; + + case eWeaponType.WEAPON_GRENADE: + case eWeaponType.WEAPON_MOLOTOV: + case eWeaponType.WEAPON_EPISODIC_8: + case eWeaponType.WEAPON_EPISODIC_16: + item.BottomLeftText = string.Format("{0}x", ammo); + break; + + case eWeaponType.WEAPON_RLAUNCHER: + item.BottomLeftText = string.Format("{0} Rockets", ammo); + break; + + default: + item.BottomLeftText = string.Format("{0} Bullets", ammo); + break; + } + } + + // Automatically add vanilla weapons to inventory + for (int i = 0; i < playerPed.WeaponData.Weapons.Length; i++) + { + GET_CHAR_WEAPON_IN_SLOT(playerPedHandle, i, out int weaponType, out int ammo0, out int ammo1); + + eWeaponType type = (eWeaponType)weaponType; + + // Ignore some weapon types + switch (type) + { + case eWeaponType.WEAPON_UNARMED: + case eWeaponType.WEAPON_WEAPONTYPE_LAST_WEAPONTYPE: + case eWeaponType.WEAPON_ARMOUR: + case eWeaponType.WEAPON_RAMMEDBYCAR: + case eWeaponType.WEAPON_RUNOVERBYCAR: + case eWeaponType.WEAPON_EXPLOSION: + case eWeaponType.WEAPON_UZI_DRIVEBY: + case eWeaponType.WEAPON_DROWNING: + case eWeaponType.WEAPON_FALL: + case eWeaponType.WEAPON_UNIDENTIFIED: + case eWeaponType.WEAPON_ANYMELEE: + case eWeaponType.WEAPON_ANYWEAPON: + continue; + } + + uint weaponTypeHash = RAGE.AtStringHash(type.ToString()); + + // Add item if it wasn't added yet + if (!playerInventory.ContainsItem(weaponTypeHash) && ammo0 != 0) + { + BasicInventoryItem item = new BasicInventoryItem(weaponTypeHash); + item.Tags.Add("IS_GAME_WEAPON", null); + item.Tags.Add("WeaponType", weaponType); + + item.PopupMenuItems.Add("Drop"); + item.TopLeftText = NativeGame.GetCommonWeaponName(type); + + if (loadedWeaponTextures.ContainsKey(weaponType)) + item.Icon = loadedWeaponTextures[weaponType]; + + playerInventory.AddItem(item); + } + } + } + + } +} diff --git a/PackAndCarry/PackAndCarry/PackAndCarry.csproj b/PackAndCarry/PackAndCarry/PackAndCarry.csproj new file mode 100644 index 0000000..98e747e --- /dev/null +++ b/PackAndCarry/PackAndCarry/PackAndCarry.csproj @@ -0,0 +1,63 @@ + + + + + Debug + AnyCPU + {9160C51B-B899-4C30-87DD-6DC7E04E079F} + Library + Properties + PackAndCarry + PackAndCarry.ivsdk + v4.8 + 512 + true + disable + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\ClonksCodingLib.GTAIV.1.9.0.38745\lib\net472\ClonksCodingLib.GTAIV.dll + + + ..\..\..\CustomInventoryIV\CustomInventoryIV\bin\Debug\CustomInventoryIV.dll + + + ..\packages\IVSDKDotNetWrapper.1.7.0\lib\net472\IVSDKDotNetWrapper.dll + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PackAndCarry/PackAndCarry/Properties/AssemblyInfo.cs b/PackAndCarry/PackAndCarry/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5ce99f3 --- /dev/null +++ b/PackAndCarry/PackAndCarry/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PackAndCarry")] +[assembly: AssemblyDescription("This is a mod which adds a custom, more advanced inventory to GTA IV.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("ItsClonkAndre")] +[assembly: AssemblyProduct("PackAndCarry")] +[assembly: AssemblyCopyright("Copyright © ItsClonkAndre 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9160c51b-b899-4c30-87dd-6dc7e04e079f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/PackAndCarry/PackAndCarry/packages.config b/PackAndCarry/PackAndCarry/packages.config new file mode 100644 index 0000000..0db1b56 --- /dev/null +++ b/PackAndCarry/PackAndCarry/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From e01a3cf3efac99007f46a838c63635b8606640ea Mon Sep 17 00:00:00 2001 From: "E. S." <39125931+ClonkAndre@users.noreply.github.com> Date: Wed, 11 Dec 2024 02:48:27 +0100 Subject: [PATCH 2/5] Update - Added custom GXT strings. - Added ability to get additional inventory slots when using different suse drawables. - Added time slowing down feature when inventory is open. - Added exhaustion feature based on the inventory weight. - Added movement speed decrease speed based on the inventory weight. - Added ability to predefine the weight of items. - Added ability to subscribe to certain inventory item events for custom items. - The inventory can no longer be opened when in a vehicle. - The API is now completely implemented. --- PackAndCarry/PACApiTest/Main.cs | 368 +++++++++ PackAndCarry/PACApiTest/PACApiTest.csproj | 68 ++ .../PACApiTest/Properties/AssemblyInfo.cs | 33 + .../Properties/Resources.Designer.cs | 73 ++ .../PACApiTest/Properties/Resources.resx | 124 +++ .../PACApiTest/Resources/crosshair.bin | Bin 0 -> 1282 bytes PackAndCarry/PACApiTest/packages.config | 4 + PackAndCarry/PackAndCarry.sln | 6 + .../PackAndCarry/Classes/Json/SuseSlots.cs | 19 + .../PackAndCarry/Classes/ModSettings.cs | 34 +- PackAndCarry/PackAndCarry/Main.cs | 724 +++++++++++++++--- PackAndCarry/PackAndCarry/PackAndCarry.csproj | 7 +- .../PackAndCarry/Properties/AssemblyInfo.cs | 6 +- PackAndCarry/PackAndCarry/packages.config | 2 +- 14 files changed, 1344 insertions(+), 124 deletions(-) create mode 100644 PackAndCarry/PACApiTest/Main.cs create mode 100644 PackAndCarry/PACApiTest/PACApiTest.csproj create mode 100644 PackAndCarry/PACApiTest/Properties/AssemblyInfo.cs create mode 100644 PackAndCarry/PACApiTest/Properties/Resources.Designer.cs create mode 100644 PackAndCarry/PACApiTest/Properties/Resources.resx create mode 100644 PackAndCarry/PACApiTest/Resources/crosshair.bin create mode 100644 PackAndCarry/PACApiTest/packages.config create mode 100644 PackAndCarry/PackAndCarry/Classes/Json/SuseSlots.cs diff --git a/PackAndCarry/PACApiTest/Main.cs b/PackAndCarry/PACApiTest/Main.cs new file mode 100644 index 0000000..7a70b06 --- /dev/null +++ b/PackAndCarry/PACApiTest/Main.cs @@ -0,0 +1,368 @@ +using System; +using System.Drawing; + +using IVSDKDotNet; + +namespace PACApiTest +{ + public class Main : Script + { + + #region Variables + public bool MenuOpen; + private ImTexture texture; + + // Inventory + public Guid PlayerInventoryID; + + // Item + private Guid addedItemID; + private string itemHash; + private string buttonText; + private string topLeftText; + private string topRightText; + private string bottomLeftText; + private string bottomRightText; + + // Tag + private string tagToAdd; + private string tagValue; + + // Popup Item + private string popupToAdd; + + // Tooltip + private string tooltipToSet; + + // Flags + private bool set; + #endregion + + #region Constructor + public Main() + { + ScriptCommandReceived += Main_ScriptCommandReceived; + OnFirstD3D9Frame += Main_OnFirstD3D9Frame; + OnImGuiRendering += Main_OnImGuiRendering; + Tick += Main_Tick; + } + #endregion + + private object Main_ScriptCommandReceived(Script fromScript, object[] args, string command) + { + switch (command) + { + case "PAC_ON_ITEM_DRAGGED_OUT": + { + Guid inventoryId = (Guid)args[0]; + Guid itemId = (Guid)args[1]; + int itemIndex = (int)args[2]; + + IVGame.Console.PrintWarningEx("Received PAC_ON_ITEM_DRAGGED_OUT script command from inventory {0} for item {1} at index {2}", inventoryId, itemId, itemIndex); + } + break; + case "PAC_ON_ITEM_DRAGGED_TO_NEW_SLOT": + { + Guid inventoryId = (Guid)args[0]; + Guid itemId = (Guid)args[1]; + int oldIndex = (int)args[2]; + int newIndex = (int)args[3]; + + IVGame.Console.PrintWarningEx("Received PAC_ON_ITEM_DRAGGED_TO_NEW_SLOT script command from inventory {0} for item {1}. Moved from slot {2} to {3}", inventoryId, itemId, oldIndex, newIndex); + } + break; + case "PAC_ON_ITEM_CLICKED": + { + Guid inventoryId = (Guid)args[0]; + Guid itemId = (Guid)args[1]; + int itemIndex = (int)args[2]; + + IVGame.Console.PrintWarningEx("Received PAC_ON_ITEM_CLICKED script command from inventory {0} for item {1} at {2}", inventoryId, itemId, itemIndex); + } + break; + case "PAC_ON_POPUP_ITEM_CLICKED": + { + Guid inventoryId = (Guid)args[0]; + Guid itemId = (Guid)args[1]; + string popupItemName = (string)args[2]; + + IVGame.Console.PrintWarningEx("Received PAC_ON_POPUP_ITEM_CLICKED script command from inventory {0} for item {1}. Clicked on {2}", inventoryId, itemId, popupItemName); + } + break; + } + + return null; + } + + private void Main_OnFirstD3D9Frame(IntPtr devicePtr) + { + ImGuiIV.CreateTextureFromMemory(Properties.Resources.crosshair, out texture, out IVSDKDotNet.Enums.eResult result); + } + private void Main_OnImGuiRendering(IntPtr devicePtr, ImGuiIV_DrawingContext ctx) + { + if (!MenuOpen) + return; + + ImGuiIV.Begin("PAC API Test", ref MenuOpen); + + // Inventory + ImGuiIV.TextDisabled("Inventory"); + + if (ImGuiIV.Button("GET_PLAYER_INVENTORY_ID")) + { + if (SendScriptCommand("PackAndCarry", "GET_PLAYER_INVENTORY_ID", null, out object result)) + { + PlayerInventoryID = (Guid)result; + IVGame.Console.PrintEx("PlayerInventoryID: {0}", PlayerInventoryID); + } + } + + if (PlayerInventoryID == Guid.Empty) + { + ImGuiIV.TextColored(Color.Yellow, "Get the player inventory id first to be able to interact with it"); + return; + } + + if (ImGuiIV.Button("GET_AMOUNT_OF_FREE_SLOTS_IN_INVENTORY")) + { + if (SendScriptCommand("PackAndCarry", "GET_AMOUNT_OF_FREE_SLOTS_IN_INVENTORY", new object[] { PlayerInventoryID }, out object result)) + { + IVGame.Console.PrintEx("Free slots in player inventory: {0}", Convert.ToInt32(result)); + } + } + + // Item + ImGuiIV.Spacing(4); + ImGuiIV.TextDisabled("Item"); + + if (ImGuiIV.TreeNode("ADD_NEW_ITEM_TO_INVENTORY")) + { + ImGuiIV.InputText("itemHash", ref itemHash); + ImGuiIV.InputText("buttonText", ref buttonText); + ImGuiIV.InputText("topLeftText", ref topLeftText); + ImGuiIV.InputText("topRightText", ref topRightText); + ImGuiIV.InputText("bottomLeftText", ref bottomLeftText); + ImGuiIV.InputText("bottomRightText", ref bottomRightText); + + if (ImGuiIV.Button("ADD_NEW_ITEM_TO_INVENTORY")) + { + object[] args = new object[] { PlayerInventoryID, Convert.ToUInt32(itemHash.Replace("0x", "")), buttonText, topLeftText, topRightText, bottomLeftText, bottomRightText }; + if (SendScriptCommand("PackAndCarry", "ADD_NEW_ITEM_TO_INVENTORY", args, out object result)) + { + addedItemID = (Guid)result; + IVGame.Console.PrintEx("Added item id: {0}", addedItemID); + } + } + + ImGuiIV.TreePop(); + } + + if (addedItemID == Guid.Empty) + { + ImGuiIV.TextColored(Color.Yellow, "Add a new item to the player inventory first to be ablet to interact with it"); + return; + } + + if (ImGuiIV.Button("REMOVE_ITEM_FROM_INVENTORY")) + { + if (SendScriptCommand("PackAndCarry", "REMOVE_ITEM_FROM_INVENTORY", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + bool res = Convert.ToBoolean(result); + IVGame.Console.PrintEx("REMOVE_ITEM_FROM_INVENTORY result: {0}", res); + + if (res) + addedItemID = Guid.Empty; + } + } + if (ImGuiIV.Button("DOES_ITEM_EXISTS_IN_INVENTORY")) + { + if (SendScriptCommand("PackAndCarry", "DOES_ITEM_EXISTS_IN_INVENTORY", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("DOES_ITEM_EXISTS_IN_INVENTORY result: {0}", Convert.ToBoolean(result)); + } + } + + // Tag + ImGuiIV.Spacing(4); + ImGuiIV.TextDisabled("Tag"); + + ImGuiIV.InputText("tagToAdd", ref tagToAdd); + ImGuiIV.InputText("tagValue", ref tagValue); + + if (ImGuiIV.Button("ADD_TAG_TO_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "ADD_TAG_TO_ITEM", new object[] { PlayerInventoryID, addedItemID, tagToAdd, tagValue }, out object result)) + { + IVGame.Console.PrintEx("ADD_TAG_TO_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + if (ImGuiIV.Button("REMOVE_TAG_FROM_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "REMOVE_TAG_FROM_ITEM", new object[] { PlayerInventoryID, addedItemID, tagToAdd }, out object result)) + { + IVGame.Console.PrintEx("REMOVE_TAG_FROM_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + if (ImGuiIV.Button("DOES_ITEM_HAVE_TAG")) + { + if (SendScriptCommand("PackAndCarry", "DOES_ITEM_HAVE_TAG", new object[] { PlayerInventoryID, addedItemID, tagToAdd }, out object result)) + { + IVGame.Console.PrintEx("DOES_ITEM_HAVE_TAG result: {0}", Convert.ToBoolean(result)); + } + } + + // Popup Item + ImGuiIV.Spacing(4); + ImGuiIV.TextDisabled("Popup Item"); + + ImGuiIV.InputText("popupToAdd", ref popupToAdd); + + if (ImGuiIV.Button("ADD_POPUP_ITEM_TO_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "ADD_POPUP_ITEM_TO_ITEM", new object[] { PlayerInventoryID, addedItemID, popupToAdd }, out object result)) + { + IVGame.Console.PrintEx("ADD_POPUP_ITEM_TO_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + if (ImGuiIV.Button("REMOVE_POPUP_ITEM_FROM_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "REMOVE_POPUP_ITEM_FROM_ITEM", new object[] { PlayerInventoryID, addedItemID, popupToAdd }, out object result)) + { + IVGame.Console.PrintEx("REMOVE_POPUP_ITEM_FROM_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + if (ImGuiIV.Button("DOES_ITEM_HAVE_POPUP_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "DOES_ITEM_HAVE_POPUP_ITEM", new object[] { PlayerInventoryID, addedItemID, popupToAdd }, out object result)) + { + IVGame.Console.PrintEx("DOES_ITEM_HAVE_POPUP_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + + // Icon + ImGuiIV.Spacing(4); + ImGuiIV.TextDisabled("Icon"); + + if (ImGuiIV.Button("ADD_ICON_TO_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "ADD_ICON_TO_ITEM", new object[] { PlayerInventoryID, addedItemID, texture.GetTexture(), texture.GetWidth(), texture.GetHeight() }, out object result)) + { + IVGame.Console.PrintEx("ADD_ICON_TO_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + if (ImGuiIV.Button("REMOVE_ICON_FROM_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "REMOVE_ICON_FROM_ITEM", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("REMOVE_ICON_FROM_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + + // Tooltip + ImGuiIV.Spacing(4); + ImGuiIV.TextDisabled("Tooltip"); + + ImGuiIV.InputText("tooltipToSet", ref tooltipToSet); + + if (ImGuiIV.Button("SET_ITEM_TOOLTIP")) + { + if (SendScriptCommand("PackAndCarry", "SET_ITEM_TOOLTIP", new object[] { PlayerInventoryID, addedItemID, tooltipToSet }, out object result)) + { + IVGame.Console.PrintEx("SET_ITEM_TOOLTIP result: {0}", Convert.ToBoolean(result)); + } + } + + // Flags + ImGuiIV.Spacing(4); + ImGuiIV.TextDisabled("Flags"); + + ImGuiIV.CheckBox("set", ref set); + + if (ImGuiIV.Button("SET_ITEM_NOT_REMOVED_FROM_INVENTORY_ON_MOD_UNLOAD")) + { + if (SendScriptCommand("PackAndCarry", "SET_ITEM_NOT_REMOVED_FROM_INVENTORY_ON_MOD_UNLOAD", new object[] { PlayerInventoryID, addedItemID, set }, out object result)) + { + IVGame.Console.PrintEx("SET_ITEM_NOT_REMOVED_FROM_INVENTORY_ON_MOD_UNLOAD result: {0}", Convert.ToBoolean(result)); + } + } + + // Events + ImGuiIV.Spacing(4); + ImGuiIV.TextDisabled("Events"); + + if (ImGuiIV.Button("SUBSCRIBE_TO_ON_ITEM_DRAGGED_OUT_EVENT_FOR_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "SUBSCRIBE_TO_ON_ITEM_DRAGGED_OUT_EVENT_FOR_ITEM", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("SUBSCRIBE_TO_ON_ITEM_DRAGGED_OUT_EVENT_FOR_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + if (ImGuiIV.Button("UNSUBSCRIBE_FROM_ON_ITEM_DRAGGED_OUT_EVENT_FOR_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "UNSUBSCRIBE_FROM_ON_ITEM_DRAGGED_OUT_EVENT_FOR_ITEM", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("UNSUBSCRIBE_FROM_ON_ITEM_DRAGGED_OUT_EVENT_FOR_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + + ImGuiIV.Spacing(2); + + if (ImGuiIV.Button("SUBSCRIBE_TO_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENT_FOR_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "SUBSCRIBE_TO_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENT_FOR_ITEM", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("SUBSCRIBE_TO_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENT_FOR_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + if (ImGuiIV.Button("UNSUBSCRIBE_FROM_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENT_FOR_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "UNSUBSCRIBE_FROM_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENT_FOR_ITEM", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("UNSUBSCRIBE_FROM_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENT_FOR_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + + ImGuiIV.Spacing(2); + + if (ImGuiIV.Button("SUBSCRIBE_TO_ON_ITEM_CLICK_EVENT_FOR_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "SUBSCRIBE_TO_ON_ITEM_CLICK_EVENT_FOR_ITEM", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("SUBSCRIBE_TO_ON_ITEM_CLICK_EVENT_FOR_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + if (ImGuiIV.Button("UNSUBSCRIBE_FROM_ON_ITEM_CLICK_EVENT_FOR_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "UNSUBSCRIBE_FROM_ON_ITEM_CLICK_EVENT_FOR_ITEM", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("UNSUBSCRIBE_FROM_ON_ITEM_CLICK_EVENT_FOR_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + + ImGuiIV.Spacing(2); + + if (ImGuiIV.Button("SUBSCRIBE_TO_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "SUBSCRIBE_TO_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("SUBSCRIBE_TO_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + if (ImGuiIV.Button("UNSUBSCRIBE_FROM_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "UNSUBSCRIBE_FROM_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("UNSUBSCRIBE_FROM_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + + ImGuiIV.End(); + } + + private void Main_Tick(object sender, EventArgs e) + { + + } + + } +} diff --git a/PackAndCarry/PACApiTest/PACApiTest.csproj b/PackAndCarry/PACApiTest/PACApiTest.csproj new file mode 100644 index 0000000..60e0afa --- /dev/null +++ b/PackAndCarry/PACApiTest/PACApiTest.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {34D3FB37-1826-44F8-A0BD-E0CC8851EE3D} + Library + Properties + PACApiTest + PACApiTest.ivsdk + v4.8 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\IVSDKDotNetWrapper.1.8.0\lib\net472\IVSDKDotNetWrapper.dll + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + \ No newline at end of file diff --git a/PackAndCarry/PACApiTest/Properties/AssemblyInfo.cs b/PackAndCarry/PACApiTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f988992 --- /dev/null +++ b/PackAndCarry/PACApiTest/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PACApiTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PACApiTest")] +[assembly: AssemblyCopyright("Copyright © 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("34d3fb37-1826-44f8-a0bd-e0cc8851ee3d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/PackAndCarry/PACApiTest/Properties/Resources.Designer.cs b/PackAndCarry/PACApiTest/Properties/Resources.Designer.cs new file mode 100644 index 0000000..b71eb91 --- /dev/null +++ b/PackAndCarry/PACApiTest/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PACApiTest.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PACApiTest.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] crosshair { + get { + object obj = ResourceManager.GetObject("crosshair", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/PackAndCarry/PACApiTest/Properties/Resources.resx b/PackAndCarry/PACApiTest/Properties/Resources.resx new file mode 100644 index 0000000..8f5ac05 --- /dev/null +++ b/PackAndCarry/PACApiTest/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\crosshair.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/PackAndCarry/PACApiTest/Resources/crosshair.bin b/PackAndCarry/PACApiTest/Resources/crosshair.bin new file mode 100644 index 0000000000000000000000000000000000000000..2fdbf17c6c73f8156224dfb8b7902fa8e3f50e5a GIT binary patch literal 1282 zcmV+d1^xPoP)EX>4Tx04R}tkv&MmKpe$iQ>9WW4t5Z6$WS|32NiKFRV;#q(pG5I!Q|2}Xws0R zxHt-~1qVMCs}3&Cx;nTDg5U>;tBaGOi)J&;^Mfxh}i>#<}dUz%#=}E;COYCKk&*EcY<07%K4$aYRuy$`|r3 zE1b7DtF;E}+>^gBRMb|oT&FpPB$kju8X{ygP(=+EVzg?cn8?t6!oxr6_|xQ)$yEg- z#{%k5Avu2VKlt6PS(=`5lR^og|HZaH#(}^t&~DoH_pxoaPXPZjaHVzq^%gMqNqV!Z zMUH@>ZQ$a%tI2!7A(Ki)<;aecM=JwXv$LRx*qpp^3fP+II zTBhuEk9QCB_V(|YPJch*4|3Cad;>55000JJOGiWi{{a60|De66lK=n!32;bRa{vG? zBLDy{BLR4&KXw2B0~1L^K~#9!?V8PM8!-@ozjYiap*F-9KR_TqJ=DSJ&AHWq_K?0w ziSNw=)bt%fNXQGc_OZ|4gpd{*ntQG-ltM|S7X?yd$&$T4T6Y9A5tQ2Wm1#JpO!6+C7qhJ(_f>AIECdIZiW8Y#mfn|Xg0*3+@j_bgKT$r&t0?UgC zWK-bS1H?2r7T8<_AT5Eb6o8D&l|ZYEKq$+^rVtk64-TJAM%2-;W~W+ zbyWnmEK&*7b?UHdBM&exN3K@j30ZSFetE=Gtj=2mK9$P}tY$g!j$`6x1YnvD`*yMn zZN9erex1N=(O`@X*Ou;AS-uflIPxH(dt1+9<0wQ=HQ-oW0-js?Zl>t@oxpucf4l-b zaSZux`F-0Q4tHK(pMd1>BA+GjH6<`xz?ZBI*1VE}Q7{Td!4%;W1s>NwEPZc)&ygJ0 zFM*GizPj&mT}|LufKlhb_Jppr1U}{D-QMG{Szyi5b)LY_th~xYTRJ2n&)1f2d8e1i z$q)QoEjs?(z(iA{X z@Q^Pv0CHK%lKBaM)&-7I0CJSug)nD8Re`pN1dcpFjLg_}=JAxsK-FT5?LhJAle-!F s79LErO~EJ_1*2dTjDk@x3Z|^>FD~rt + + + \ No newline at end of file diff --git a/PackAndCarry/PackAndCarry.sln b/PackAndCarry/PackAndCarry.sln index ae1482d..f9d2857 100644 --- a/PackAndCarry/PackAndCarry.sln +++ b/PackAndCarry/PackAndCarry.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.9.34728.123 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackAndCarry", "PackAndCarry\PackAndCarry.csproj", "{9160C51B-B899-4C30-87DD-6DC7E04E079F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PACApiTest", "PACApiTest\PACApiTest.csproj", "{34D3FB37-1826-44F8-A0BD-E0CC8851EE3D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {9160C51B-B899-4C30-87DD-6DC7E04E079F}.Debug|Any CPU.Build.0 = Debug|Any CPU {9160C51B-B899-4C30-87DD-6DC7E04E079F}.Release|Any CPU.ActiveCfg = Release|Any CPU {9160C51B-B899-4C30-87DD-6DC7E04E079F}.Release|Any CPU.Build.0 = Release|Any CPU + {34D3FB37-1826-44F8-A0BD-E0CC8851EE3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34D3FB37-1826-44F8-A0BD-E0CC8851EE3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34D3FB37-1826-44F8-A0BD-E0CC8851EE3D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34D3FB37-1826-44F8-A0BD-E0CC8851EE3D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PackAndCarry/PackAndCarry/Classes/Json/SuseSlots.cs b/PackAndCarry/PackAndCarry/Classes/Json/SuseSlots.cs new file mode 100644 index 0000000..aaf68af --- /dev/null +++ b/PackAndCarry/PackAndCarry/Classes/Json/SuseSlots.cs @@ -0,0 +1,19 @@ +namespace PackAndCarry.Classes.Json +{ + internal class SuseSlots + { + + #region Variables + public uint Index; + public byte AdditionalSlots; + #endregion + + #region Constructor + public SuseSlots() + { + + } + #endregion + + } +} diff --git a/PackAndCarry/PackAndCarry/Classes/ModSettings.cs b/PackAndCarry/PackAndCarry/Classes/ModSettings.cs index 0dfcb8f..6f3d542 100644 --- a/PackAndCarry/PackAndCarry/Classes/ModSettings.cs +++ b/PackAndCarry/PackAndCarry/Classes/ModSettings.cs @@ -1,6 +1,4 @@ -using System.Numerics; - -using IVSDKDotNet; +using IVSDKDotNet; namespace PackAndCarry.Classes { @@ -10,18 +8,48 @@ internal class ModSettings #region Variables // General public static bool DisableInMP; + public static bool UseCustomGXTs; // Inventory public static int DefaultCapacity; + public static bool AllowExtraSlots; + + // Time + public static bool SlowDownTimeWhenInventoryIsOpen; + public static float SlowDownTimeAmount; + + // Exhaustion + public static bool DisableExhaustionInMP; + public static bool PlayerIsExhaustedMoreQuicklyBasedOnInventoryWeight; + + // Movement + public static bool DisableMovementImpactInMP; + public static bool PlayerMovementSpeedDecreasedByInventoryWeight; + public static float MovementSpeedDecreaseMultiplier; #endregion public static void Load(SettingsFile settings) { // General DisableInMP = settings.GetBoolean("General", "DisableInMP", false); + UseCustomGXTs = settings.GetBoolean("General", "UseCustomGXTs", true); // Inventory DefaultCapacity = settings.GetInteger("Inventory", "DefaultCapacity", 10); + AllowExtraSlots = settings.GetBoolean("Inventory", "AllowExtraSlots", true); + + // Time + SlowDownTimeWhenInventoryIsOpen = settings.GetBoolean("Time", "SlowDownTimeWhenInventoryIsOpen", true); + SlowDownTimeAmount = settings.GetFloat("Time", "SlowDownTimeAmount", 0.25f); + + // Exhaustion + DisableExhaustionInMP = settings.GetBoolean("Exhaustion", "DisableInMP", true); + PlayerIsExhaustedMoreQuicklyBasedOnInventoryWeight = settings.GetBoolean("Exhaustion", "PlayerIsExhaustedMoreQuicklyBasedOnInventoryWeight", false); + + // Movement + DisableMovementImpactInMP = settings.GetBoolean("Movement", "DisableInMP", true); + PlayerMovementSpeedDecreasedByInventoryWeight = settings.GetBoolean("Movement", "PlayerMovementSpeedDecreasedByInventoryWeight", false); + MovementSpeedDecreaseMultiplier = settings.GetFloat("Movement", "DecreaseMultiplier", 1.0f); } } diff --git a/PackAndCarry/PackAndCarry/Main.cs b/PackAndCarry/PackAndCarry/Main.cs index a850d9e..3a0ce45 100644 --- a/PackAndCarry/PackAndCarry/Main.cs +++ b/PackAndCarry/PackAndCarry/Main.cs @@ -3,8 +3,10 @@ using System.Diagnostics; using System.Drawing; using System.IO; +using System.Linq; using System.Numerics; using System.Windows.Forms; +using System.Reflection; using System.Runtime.InteropServices; using CCL.GTAIV; @@ -14,6 +16,7 @@ using CustomInventoryIV.Inventories; using PackAndCarry.Classes; +using PackAndCarry.Classes.Json; using IVSDKDotNet; using IVSDKDotNet.Enums; @@ -32,7 +35,10 @@ public class Main : Script #region Variables // Lists private InventoryPool inventoryPool; - private Dictionary loadedWeaponTextures; + private Queue mainThreadQueue; + private Dictionary loadedWeaponTextures; + private Dictionary> playerSuseSlots; + private Dictionary predefinedItemWeights; // Inventory stuff private BasicInventory playerInventory; @@ -44,16 +50,191 @@ public class Main : Script private bool blockPlayerAbilityToCollectPickup; private Vector3 lastPickupPosition; + // Interpolation + private float timeScaleInterpolationValue; + private float lastTimeScaleValue; + + private float moveAnimSpeedInterpolationValue; + private float lastMoveAnimSpeedValue; + // Other + private ConstructorInfo imTextureCtor; + private int playerPedHandle; private int lastPlayerWeapon; private uint lastLoadedEpisode; - private float timeScaleInterpolationValue; private bool wasCursorPosSet; + private bool wasROM614Displayed; + private bool wasROM613Displayed; #endregion #region Methods + private void UnloadTextures() + { + foreach (KeyValuePair item in loadedWeaponTextures) + { + item.Value.Release(); + } + loadedWeaponTextures.Clear(); + } + private void LoadTextures() + { + if (loadedWeaponTextures.Count != 0) + { + // Destroy loaded textures if changing episode + if (lastLoadedEpisode != IVGame.CurrentEpisodeMenu) + { + UnloadTextures(); + } + } + + // Set episode + lastLoadedEpisode = IVGame.CurrentEpisodeMenu; + + string path = string.Format("{0}\\Icons\\Weapons\\{1}", ScriptResourceFolder, lastLoadedEpisode); + + // Create textures for the current episode + string[] files = Directory.GetFiles(path, "*.dds", SearchOption.TopDirectoryOnly); + for (int i = 0; i < files.Length; i++) + { + string file = files[i]; + string fileName = Path.GetFileName(file); + + if (int.TryParse(fileName.Split('.')[0], out int result)) + { + // Create texture + if (ImGuiIV.CreateTextureFromFile(string.Format("{0}\\{1}", path, fileName), out ImTexture texture, out eResult r)) + { + loadedWeaponTextures.Add(result, texture); + } + else + { + IVGame.Console.PrintError(string.Format("Failed to create texture {0}! Result: {1}", fileName, r)); + } + } + } + } + private void LoadPlayerSuseSlots() + { + try + { + string fileName = string.Format("{0}\\Data\\playerSuseSlots.json", ScriptResourceFolder); + + if (!File.Exists(fileName)) + { + Logging.LogWarning("Could not find playerSuseSlots.json file! Some features might not be available."); + return; + } + + if (playerSuseSlots != null) + playerSuseSlots.Clear(); + + playerSuseSlots = Helper.ConvertJsonStringToObject>>(File.ReadAllText(fileName)); + + if (playerSuseSlots.Count == 0) + Logging.LogWarning("Loaded 0 player suse slot(s)! Some features might not be available."); + else + Logging.Log("Loaded {0} player suse slot(s)!", playerSuseSlots.Count); + } + catch (Exception ex) + { + Logging.LogError("An error occured while trying to load player suse slots! Details: {0}", ex); + } + } + private void LoadPredefiniedItemWeights() + { + try + { + string fileName = string.Format("{0}\\Data\\predefinedItemWeights.json", ScriptResourceFolder); + + if (!File.Exists(fileName)) + { + Logging.LogWarning("Could not find predefinedItemWeights.json file! Some features might not be available."); + return; + } + + if (predefinedItemWeights != null) + predefinedItemWeights.Clear(); + + predefinedItemWeights = Helper.ConvertJsonStringToObject>(File.ReadAllText(fileName)); + + if (predefinedItemWeights.Count == 0) + Logging.LogWarning("Loaded 0 predefined item weight(s)! Some features might not be available."); + else + Logging.Log("Loaded {0} predefined item weight(s)!", predefinedItemWeights.Count); + } + catch (Exception ex) + { + Logging.LogError("An error occured while trying to load predefined item weights! Details: {0}", ex); + } + } + + private void DisplayCustomMissionIntroductionText() + { + // Show custom ROM6_14 help message + if (IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("ROM6_14")) + { + if (!wasROM614Displayed) + { + string originalStr = GET_STRING_FROM_TEXT_FILE("ROM6_14"); + + switch ((eLanguage)GET_CURRENT_LANGUAGE()) + { + case eLanguage.LANGUAGE_AMERICAN: + NativeGame.DisplayCustomHelpMessage(originalStr + " Press ~INPUT_FRONTEND_LEGEND~ to quickly switch to your last equipped weapon. Hold ~INPUT_FRONTEND_LEGEND~ to open up the inventory.", true); + break; + case eLanguage.LANGUAGE_GERMAN: + NativeGame.DisplayCustomHelpMessage(originalStr + " Drücke ~INPUT_FRONTEND_LEGEND~, um schnell zu der zuletzt ausgerüsteten Waffe zu wechseln. Halte ~INPUT_FRONTEND_LEGEND~ gedrückt, um das Inventar zu öffnen.", true); + break; + case eLanguage.LANGUAGE_ITALIAN: + NativeGame.DisplayCustomHelpMessage(originalStr + " Premi ~INPUT_FRONTEND_LEGEND~ per passare rapidamente all'ultima arma equipaggiata. Tieni premuto ~INPUT_FRONTEND_LEGEND~ per aprire l'inventario.", true); + break; + case eLanguage.LANGUAGE_SPANISH: + NativeGame.DisplayCustomHelpMessage(originalStr + " Presiona ~INPUT_FRONTEND_LEGEND~ para cambiar rápidamente a la última arma equipada. Mantén presionado ~INPUT_FRONTEND_LEGEND~ para abrir el inventario.", true); + break; + } + + wasROM614Displayed = true; + } + } + else + { + wasROM614Displayed = false; + } + + // Show custom ROM6_13 help message + if (IS_THIS_HELP_MESSAGE_BEING_DISPLAYED("ROM6_13")) + { + if (!wasROM613Displayed) + { + string originalStr = GET_STRING_FROM_TEXT_FILE("ROM6_13"); + + switch ((eLanguage)GET_CURRENT_LANGUAGE()) + { + case eLanguage.LANGUAGE_AMERICAN: + NativeGame.DisplayCustomHelpMessage(originalStr.Replace("weapon inventory", "inventory")); + break; + case eLanguage.LANGUAGE_GERMAN: + NativeGame.DisplayCustomHelpMessage(originalStr.Replace("Waffeninventar", "Inventar")); + break; + case eLanguage.LANGUAGE_ITALIAN: + NativeGame.DisplayCustomHelpMessage(originalStr.Replace(" delle armi", "")); + break; + case eLanguage.LANGUAGE_SPANISH: + NativeGame.DisplayCustomHelpMessage(originalStr.Replace(" de armas", "")); + break; + } + + wasROM613Displayed = true; + } + } + else + { + wasROM613Displayed = false; + } + } + private void OpenInventory(bool wasOpenedUsingController) { playerInventory.IsVisible = true; @@ -67,8 +248,71 @@ private void CloseInventory() wasInventoryOpenedViaController = false; } + private void ResizeInventoryBasedOnClothes() + { + if (!ModSettings.AllowExtraSlots) + return; + if (playerInventory.IsVisible) + return; + + uint drawable = GET_CHAR_DRAWABLE_VARIATION(playerPedHandle, 3); + + if (drawable == 0) + { + playerInventory.Resize(ModSettings.DefaultCapacity); + return; + } + + // Try get player suse slots list for current episode + if (playerSuseSlots.TryGetValue(GET_CURRENT_EPISODE(), out List slots)) + { + if (slots == null) + return; + + // Try get suse slots for current suse drawable + SuseSlots suseSlot = slots.Where(x => x.Index == drawable).FirstOrDefault(); + + if (suseSlot == null) + return; + + // Check if inventory already has the new capacity + int newCapacity = ModSettings.DefaultCapacity + suseSlot.AdditionalSlots; + + if (playerInventory.Capacity != newCapacity) + playerInventory.Resize(newCapacity); + } + } + private void CheckForInvalidScriptItems() + { + // Checks if the scripts that added their own items to the inventory are still running + + BasicInventoryItem[] items = playerInventory.GetItems(); + + for (int i = 0; i < items.Length; i++) + { + BasicInventoryItem item = items[i]; + + if (item == null) + continue; + if (!item.Tags.ContainsKey("IS_CUSTOM_ITEM")) + continue; + if (item.Tags.ContainsKey("DO_NOT_REMOVE_ITEM_WHEN_MOD_THAT_ADDED_IT_UNLOADS")) + continue; + + Guid ownerScriptId = (Guid)item.Tags["IS_CUSTOM_ITEM"]; + + // Check if owner script is still running + if (!IsScriptRunning(ownerScriptId)) + { + Logging.LogDebug("Removing custom item {0} because its owner script is no longer running.", item.ID, ownerScriptId); + playerInventory.RemoveItem(item); + } + } + } private void HandleTimescaleInterpolation() { + if (!ModSettings.SlowDownTimeWhenInventoryIsOpen) + return; if (IVNetwork.IsNetworkGameRunning()) return; @@ -77,17 +321,33 @@ private void HandleTimescaleInterpolation() timeScaleInterpolationValue = timeScaleInterpolationValue + 0.02f; if (timeScaleInterpolationValue > 1.0f) + { timeScaleInterpolationValue = 1.0f; + } + else + { + float newTimeScale = IVTimer.TimeScale - ModSettings.SlowDownTimeAmount; + + if (newTimeScale > 0.0f) + { + lastTimeScaleValue = newTimeScale; + IVTimer.TimeScale = Lerp(1.0f, newTimeScale, timeScaleInterpolationValue); + } + } } else { timeScaleInterpolationValue = timeScaleInterpolationValue - 0.03f; if (timeScaleInterpolationValue < 0.0f) + { timeScaleInterpolationValue = 0.0f; + } + else + { + IVTimer.TimeScale = Lerp(1.0f, lastTimeScaleValue, timeScaleInterpolationValue); + } } - - IVTimer.TimeScale = Lerp(1.0f, 0.25f, timeScaleInterpolationValue); } private void HandlePreventPlayerToCollectPickup() { @@ -107,6 +367,73 @@ private void HandlePreventPlayerToCollectPickup() DISABLE_LOCAL_PLAYER_PICKUPS(true); } } + private void HandlePlayerImpacts(IVPed playerPed) + { + if (!ModSettings.PlayerIsExhaustedMoreQuicklyBasedOnInventoryWeight && !ModSettings.PlayerMovementSpeedDecreasedByInventoryWeight) + return; + + // Check if player is pressing the sprint key + if (NativeControls.IsGameKeyPressed(0, GameKey.Sprint)) + { + float weight = GetTotalInventoryWeight(playerInventory); + + if (weight <= 0f) + return; + + // Calculate how much the move anim speed will be decreased + float moveAnimSpeedDecrease = Math.Max(0.0f, (1.0f - Math.Min(1.0f, weight / 1000f)) * ModSettings.MovementSpeedDecreaseMultiplier); + lastMoveAnimSpeedValue = moveAnimSpeedDecrease; + + // Handle exhaustion + if (ModSettings.PlayerIsExhaustedMoreQuicklyBasedOnInventoryWeight && !(ModSettings.DisableExhaustionInMP && IVNetwork.IsNetworkGameRunning())) + { + IVPlayerInfo playerInfo = IVPlayerInfo.GetPlayerInfo(GET_PLAYER_ID()); + + // Calculate how long the player will be exhausted for + float minStamina = -((15f / moveAnimSpeedDecrease) * 10f); + + // Decrease stamina faster depending on the weight of the inventory + float stamina = playerInfo.Stamina; + + if (!(stamina < minStamina)) + playerInfo.Stamina -= weight / 100; + } + + // Handle move anim speed impact + if (ModSettings.PlayerMovementSpeedDecreasedByInventoryWeight && !(ModSettings.DisableMovementImpactInMP && IVNetwork.IsNetworkGameRunning())) + { + // Interpolate move anim speed + moveAnimSpeedInterpolationValue = moveAnimSpeedInterpolationValue + 0.01f; + + if (moveAnimSpeedInterpolationValue > 1.0f) + { + moveAnimSpeedInterpolationValue = 1.0f; + } + else + { + // Set new move anim speed + playerPed.PedMoveBlendOnFoot.MoveAnimSpeed = Lerp(1.0f, moveAnimSpeedDecrease, moveAnimSpeedInterpolationValue); + } + } + + //ShowSubtitleMessage("Raw Weight: {0}, moveAnimSpeedDecrease: {1}, Stamina: {2}, MinStamina: {3}", weight, moveAnimSpeedDecrease, stamina, minStamina); + } + else + { + // Interpolate move anim speed + moveAnimSpeedInterpolationValue = moveAnimSpeedInterpolationValue - 0.01f; + + if (moveAnimSpeedInterpolationValue < 0.0f) + { + moveAnimSpeedInterpolationValue = 0.0f; + } + else + { + // Set new move anim speed + playerPed.PedMoveBlendOnFoot.MoveAnimSpeed = Lerp(1.0f, lastMoveAnimSpeedValue, moveAnimSpeedInterpolationValue); + } + } + } private void DropItem(BasicInventory inventory, BasicInventoryItem item, float range = 0.0F) { @@ -138,46 +465,32 @@ private void DropItem(BasicInventory inventory, BasicInventoryItem item, float r inventory.RemoveItem(item); } - private void LoadTextures() + private void BeginInvokeItemEventForSubscriber(BasicInventoryItem targetItem, string eventTag, string scriptCommand, object[] args) { - if (loadedWeaponTextures.Count != 0) + try { - // Destroy loaded textures if changing episode - if (lastLoadedEpisode != IVGame.CurrentEpisodeMenu) - { - foreach (KeyValuePair item in loadedWeaponTextures) - { - IntPtr texture = item.Value.GetTexture(); - ImGuiIV.ReleaseTexture(ref texture); - } - loadedWeaponTextures.Clear(); - } - } - - // Set episode - lastLoadedEpisode = IVGame.CurrentEpisodeMenu; + Guid targetScriptId = (Guid)targetItem.Tags[eventTag]; - string path = string.Format("{0}\\Icons\\Weapons\\{1}", ScriptResourceFolder, lastLoadedEpisode); + if (targetScriptId == Guid.Empty) + return; - // Create textures for the current episode - string[] files = Directory.GetFiles(path, "*.dds", SearchOption.TopDirectoryOnly); - for (int i = 0; i < files.Length; i++) - { - string file = files[i]; - string fileName = Path.GetFileName(file); + Guid itemId = targetItem.ID; - if (int.TryParse(fileName.Split('.')[0], out int result)) + mainThreadQueue.Enqueue(() => { - // Create texture - if (ImGuiIV.CreateTextureFromFile(string.Format("{0}\\{1}", path, fileName), out IntPtr txtPtr, out int w, out int h, out eResult r)) + if (SendScriptCommand(targetScriptId, scriptCommand, args, out object result)) { - loadedWeaponTextures.Add(result, new CITexture(txtPtr, new Size(w, h))); + Logging.LogDebug("'{0}' was sent to script '{1}' for item '{2}'.", scriptCommand, targetScriptId, itemId); } else { - IVGame.Console.PrintError(string.Format("Failed to create texture {0}! Result: {1}", fileName, r)); + Logging.LogDebug("Could not send '{0}' command to script '{1}'!", scriptCommand, targetScriptId); } - } + }); + } + catch (Exception ex) + { + Logging.LogError("Failed to send a script command to another script which wishes to receive event notifications. Details: {0}", ex); } } #endregion @@ -198,6 +511,12 @@ private int CreateWeaponPickupAtPosition(Vector3 pos, int weaponType, int ammo) Vector3 spawnPos = NativeWorld.GetGroundPosition(pos) + new Vector3(0f, 0f, 0.05f); CREATE_PICKUP_ROTATE(model, (uint)ePickupType.PICKUP_TYPE_WEAPON, (uint)ammo, spawnPos, new Vector3(90f, 0f, GENERATE_RANDOM_FLOAT_IN_RANGE(0f, 90f)), out int pickup); + // Add pickup to current interior the player is in + GET_KEY_FOR_CHAR_IN_ROOM(playerPedHandle, out uint key); + + if (key != 0) + ADD_PICKUP_TO_INTERIOR_ROOM_BY_KEY(pickup, key); + // DOES_PICKUP_EXIST // REMOVE_PICKUP @@ -215,29 +534,103 @@ private BasicInventory FindInventory(Guid id) // Convert inventory base to basic inventory return (BasicInventory)inventoryBase; } + + private BasicInventoryItem[] GetAllItemsWhichGotThisTag(BasicInventory inventory, string tag) + { + BasicInventoryItem[] items = inventory.GetItems(); + return items.Where(x => x.Tags.ContainsKey(tag)).ToArray(); + } + public float GetTotalInventoryWeight(BasicInventory inventory) + { + BasicInventoryItem[] items = GetAllItemsWhichGotThisTag(inventory, "WEIGHT"); + + float weight = 0.0f; + + for (int i = 0; i < items.Length; i++) + weight += Convert.ToSingle(items[i].Tags["WEIGHT"]); + + return weight; + } + + private bool SubscribeToItemEvent(Guid inventoryId, Guid itemId, Guid targetScriptId, string eventTag) + { + // Find target inventory + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Check if tag is already present + if (item.Tags.ContainsKey(eventTag)) + return false; + + // Add tag to item + item.Tags.Add(eventTag, targetScriptId); + + return true; + } + private bool UnsubscribeFromItemEvent(Guid inventoryId, Guid itemId, string eventTag) + { + // Find target inventory + BasicInventory inventory = FindInventory(inventoryId); + + if (inventory == null) + return false; + + // Find target item + BasicInventoryItem item = inventory.GetItem(itemId); + + if (item == null) + return false; + + // Check if tag is present + if (!item.Tags.ContainsKey(eventTag)) + return false; + + // Remove tag from item + return item.Tags.Remove(eventTag); + } #endregion #region Events private void BasicInventory_OnItemDraggedOut(BasicInventory sender, BasicInventoryItem item, int itemIndex) { - if (sender.Name == "TestInventory") + if (item.Tags.ContainsKey("IS_GAME_WEAPON")) { DropItem(sender, item); } + + // Raise API "PAC_ON_ITEM_DRAGGED_OUT" event for subscriber + if (item.Tags.ContainsKey("WANTS_ON_ITEM_DRAGGED_OUT_EVENTS")) + BeginInvokeItemEventForSubscriber(item, "WANTS_ON_ITEM_DRAGGED_OUT_EVENTS", "PAC_ON_ITEM_DRAGGED_OUT", new object[] { sender.ID, item.ID, itemIndex }); } private void BasicInventory_OnItemDraggedToNewSlot(BasicInventory sender, BasicInventoryItem item, int oldIndex, int newIndex) { - IVGame.Console.PrintWarning(string.Format("Item {0} was dragged from slot {1} to {2}", item.TopLeftText, oldIndex, newIndex)); + //IVGame.Console.PrintWarning(string.Format("Item {0} was dragged from slot {1} to {2}", item.TopLeftText, oldIndex, newIndex)); + + // Raise API "PAC_ON_ITEM_DRAGGED_TO_NEW_SLOT" event for subscriber + if (item.Tags.ContainsKey("WANTS_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENTS")) + BeginInvokeItemEventForSubscriber(item, "WANTS_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENTS", "PAC_ON_ITEM_DRAGGED_TO_NEW_SLOT", new object[] { sender.ID, item.ID, oldIndex, newIndex }); } private void BasicInventory_OnPopupItemClick(BasicInventory sender, BasicInventoryItem item, string popupItemName) { - if (sender.Name == "TestInventory") + if (item.Tags.ContainsKey("IS_GAME_WEAPON")) { if (popupItemName == "Drop") { DropItem(sender, item); } } + + // Raise API "PAC_ON_POPUP_ITEM_CLICKED" event for subscriber + if (item.Tags.ContainsKey("WANTS_ON_POPUP_ITEM_CLICK_EVENTS")) + BeginInvokeItemEventForSubscriber(item, "WANTS_ON_POPUP_ITEM_CLICK_EVENTS", "PAC_ON_POPUP_ITEM_CLICKED", new object[] { sender.ID, item.ID, popupItemName }); } private void BasicInventory_OnItemClick(BasicInventory sender, BasicInventoryItem item, int itemIndex) { @@ -248,34 +641,17 @@ private void BasicInventory_OnItemClick(BasicInventory sender, BasicInventoryIte SET_CURRENT_CHAR_WEAPON(playerPedHandle, weaponType, false); } + // Raise API "PAC_ON_ITEM_CLICKED" event for subscriber + if (item.Tags.ContainsKey("WANTS_ON_ITEM_CLICK_EVENTS")) + BeginInvokeItemEventForSubscriber(item, "WANTS_ON_ITEM_CLICK_EVENTS", "PAC_ON_ITEM_CLICKED", new object[] { sender.ID, item.ID, itemIndex }); + + // Close inventory when inventory was opened using a controller if (wasInventoryOpenedViaController) CloseInventory(); - - //if (itemIndex == 0) - //{ - // sender.Resize(12); - //} - //else if (itemIndex == 1) - //{ - // List leftBehindItems = sender.Resize(8); - - // if (leftBehindItems != null) - // { - // IVGame.Console.PrintError(string.Format("There are {0} left behind items:", leftBehindItems.Count)); - // for (int i = 0; i < leftBehindItems.Count; i++) - // { - // BasicInventoryItem leftBehindItem = leftBehindItems[i]; - // IVGame.Console.PrintWarning(leftBehindItem.TopLeftText); - // } - // } - // else - // { - // IVGame.Console.PrintError("There where no left behind items."); - // } - //} } private void BasicInventory_OnInventoryResized(BasicInventory target, List leftBehindItems) { + // Process any left behind items if (leftBehindItems != null) { for (int i = 0; i < leftBehindItems.Count; i++) @@ -283,7 +659,7 @@ private void BasicInventory_OnInventoryResized(BasicInventory target, List(32); + mainThreadQueue = new Queue(); + loadedWeaponTextures = new Dictionary(32); + playerSuseSlots = new Dictionary>(); + predefinedItemWeights = new Dictionary(); // Other inventoryKeyWatch = new Stopwatch(); + // Find constructor of the ImTexture class + // This is a little hack until the ImTexture class exposes its constructor, or if it provides a way of creating a new instance of it + imTextureCtor = typeof(ImTexture).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(IntPtr), typeof(Size) }, Array.Empty()); + Logging.LogDebug("Found ImTexture Constructor: {0}", imTextureCtor != null); + // IV-SDK .NET stuff + ForceNoAbort = true; Uninitialize += Main_Uninitialize; Initialized += Main_Initialized; GameLoad += Main_GameLoad; OnImGuiRendering += Main_OnImGuiRendering; ScriptCommandReceived += Main_ScriptCommandReceived; + ProcessPad += Main_ProcessPad; Tick += Main_Tick; } #endregion private void Main_Uninitialize(object sender, EventArgs e) { + IVTimer.TimeScale = 1.0f; + + UnloadTextures(); + if (loadedWeaponTextures != null) { loadedWeaponTextures.Clear(); @@ -329,6 +719,8 @@ private void Main_Uninitialize(object sender, EventArgs e) private void Main_Initialized(object sender, EventArgs e) { ModSettings.Load(Settings); + LoadPlayerSuseSlots(); + LoadPredefiniedItemWeights(); // Create inventory pool inventoryPool = new InventoryPool(); @@ -376,6 +768,9 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri case "GET_AMOUNT_OF_FREE_SLOTS_IN_INVENTORY": { + if (args == null) + return -1; + Guid inventoryId = (Guid)args[0]; // Find target inventory @@ -390,6 +785,9 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri // Item case "ADD_NEW_ITEM_TO_INVENTORY": { + if (args == null) + return Guid.Empty; + Guid inventoryId = (Guid)args[0]; // Find target inventory @@ -425,13 +823,16 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri return Guid.Empty; } - item.Tags.Add("IS_CUSTOM_ITEM", null); - item.Tags.Add("OWNER_SCRIPT_ID", fromScript.ID); // TODO: NEED TO ADD A WAY FOR SCRIPTS TO CHANGE THEIR ID + item.Tags.Add("IS_CUSTOM_ITEM", fromScript.ID); + //item.Tags.Add("OWNER_SCRIPT_ID", fromScript.ID); return item.ID; } case "REMOVE_ITEM_FROM_INVENTORY": { + if (args == null) + return false; + // Find target inventory Guid inventoryId = (Guid)args[0]; @@ -457,8 +858,12 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri return inventory.ContainsItem((Guid)args[1]); } + // Tag case "ADD_TAG_TO_ITEM": { + if (args == null) + return false; + // Find target inventory Guid inventoryId = (Guid)args[0]; @@ -488,6 +893,9 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri } case "REMOVE_TAG_FROM_ITEM": { + if (args == null) + return false; + // Find target inventory Guid inventoryId = (Guid)args[0]; @@ -515,6 +923,9 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri } case "DOES_ITEM_HAVE_TAG": { + if (args == null) + return false; + // Find target inventory Guid inventoryId = (Guid)args[0]; @@ -535,8 +946,12 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri return item.Tags.ContainsKey(Convert.ToString(args[2])); } + // Popup Item case "ADD_POPUP_ITEM_TO_ITEM": { + if (args == null) + return false; + // Find target inventory Guid inventoryId = (Guid)args[0]; @@ -566,6 +981,9 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri } case "REMOVE_POPUP_ITEM_FROM_ITEM": { + if (args == null) + return false; + // Find target inventory Guid inventoryId = (Guid)args[0]; @@ -593,6 +1011,9 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri } case "DOES_ITEM_HAVE_POPUP_ITEM": { + if (args == null) + return false; + // Find target inventory Guid inventoryId = (Guid)args[0]; @@ -613,8 +1034,12 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri return item.PopupMenuItems.Contains(Convert.ToString(args[2])); } + // Icon case "ADD_ICON_TO_ITEM": { + if (args == null) + return false; + // Find target inventory Guid inventoryId = (Guid)args[0]; @@ -637,12 +1062,20 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri int height = Convert.ToInt32(args[4]); // Set icon - item.Icon = new CITexture(texture, new Size(width, height)); + object rawObj = imTextureCtor.Invoke(new object[] { texture, new Size(width, height) }); + + if (rawObj == null) + return false; + + item.Icon = (ImTexture)rawObj; return true; } case "REMOVE_ICON_FROM_ITEM": { + if (args == null) + return false; + // Find target inventory Guid inventoryId = (Guid)args[0]; @@ -665,8 +1098,12 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri return true; } - case "SET_ITEM_NOT_REMOVED_FROM_INVENTORY_ON_MOD_UNLOAD": + // Tooltip + case "SET_ITEM_TOOLTIP": { + if (args == null) + return false; + // Find target inventory Guid inventoryId = (Guid)args[0]; @@ -683,26 +1120,18 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri if (item == null) return false; - bool set = Convert.ToBoolean(args[2]); - - // Check if tag is already present - string tag = "DO_NOT_REMOVE_ITEM_WHEN_MOD_THAT_ADDED_IT_UNLOADS"; - - if (set) - { - if (!item.Tags.ContainsKey(tag)) - item.Tags.Add(tag, null); - } - else - { - if (item.Tags.ContainsKey(tag)) - item.Tags.Remove(tag); - } + // Set tooltip + item.ButtonTooltip = Convert.ToString(args[2]); return true; } - case "SET_ITEM_TOOLTIP": + + // Flags + case "SET_ITEM_NOT_REMOVED_FROM_INVENTORY_ON_MOD_UNLOAD": { + if (args == null) + return false; + // Find target inventory Guid inventoryId = (Guid)args[0]; @@ -719,70 +1148,108 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri if (item == null) return false; - // Set tooltip - item.ButtonTooltip = Convert.ToString(args[2]); + bool set = Convert.ToBoolean(args[2]); + + // Check if tag is already present + string tag = "DO_NOT_REMOVE_ITEM_WHEN_MOD_THAT_ADDED_IT_UNLOADS"; + + if (set) + { + if (!item.Tags.ContainsKey(tag)) + item.Tags.Add(tag, null); + } + else + { + if (item.Tags.ContainsKey(tag)) + item.Tags.Remove(tag); + } return true; } - // Events (TODO) - case "SUBSCRIBE_TO_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM": + // Events + case "SUBSCRIBE_TO_ON_ITEM_DRAGGED_OUT_EVENT_FOR_ITEM": { - // Find target inventory - Guid inventoryId = (Guid)args[0]; + if (args == null) + return false; - BasicInventory inventory = FindInventory(inventoryId); + Guid inventoryId = (Guid)args[0]; + Guid itemId = (Guid)args[1]; - if (inventory == null) + return SubscribeToItemEvent(inventoryId, itemId, fromScript.ID, "WANTS_ON_ITEM_DRAGGED_OUT_EVENTS"); + } + case "UNSUBSCRIBE_FROM_ON_ITEM_DRAGGED_OUT_EVENT_FOR_ITEM": + { + if (args == null) return false; - // Find target item + Guid inventoryId = (Guid)args[0]; Guid itemId = (Guid)args[1]; - BasicInventoryItem item = inventory.GetItem(itemId); + return UnsubscribeFromItemEvent(inventoryId, itemId, "WANTS_ON_ITEM_DRAGGED_OUT_EVENTS"); + } - if (item == null) + case "SUBSCRIBE_TO_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENT_FOR_ITEM": + { + if (args == null) return false; - // Check if tag is already present - string tag = "WANTS_ON_POPUP_ITEM_CLICK_EVENTS"; + Guid inventoryId = (Guid)args[0]; + Guid itemId = (Guid)args[1]; - if (item.Tags.ContainsKey(tag)) + return SubscribeToItemEvent(inventoryId, itemId, fromScript.ID, "WANTS_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENTS"); + } + case "UNSUBSCRIBE_FROM_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENT_FOR_ITEM": + { + if (args == null) return false; - // Add tag to item - item.Tags.Add(tag, fromScript.ID); // TODO: NEED TO ADD A WAY FOR SCRIPTS TO CHANGE THEIR ID + Guid inventoryId = (Guid)args[0]; + Guid itemId = (Guid)args[1]; - return true; + return UnsubscribeFromItemEvent(inventoryId, itemId, "WANTS_ON_ITEM_DRAGGED_TO_NEW_SLOT_EVENTS"); } - case "UNSUBSCRIBE_FROM_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM": + + case "SUBSCRIBE_TO_ON_ITEM_CLICK_EVENT_FOR_ITEM": { - // Find target inventory - Guid inventoryId = (Guid)args[0]; + if (args == null) + return false; - BasicInventory inventory = FindInventory(inventoryId); + Guid inventoryId = (Guid)args[0]; + Guid itemId = (Guid)args[1]; - if (inventory == null) + return SubscribeToItemEvent(inventoryId, itemId, fromScript.ID, "WANTS_ON_ITEM_CLICK_EVENTS"); + } + case "UNSUBSCRIBE_FROM_ON_ITEM_CLICK_EVENT_FOR_ITEM": + { + if (args == null) return false; - // Find target item + Guid inventoryId = (Guid)args[0]; Guid itemId = (Guid)args[1]; - BasicInventoryItem item = inventory.GetItem(itemId); + return UnsubscribeFromItemEvent(inventoryId, itemId, "WANTS_ON_ITEM_CLICK_EVENTS"); + } - if (item == null) + case "SUBSCRIBE_TO_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM": + { + if (args == null) return false; - // Check if tag is present - string tag = "WANTS_ON_POPUP_ITEM_CLICK_EVENTS"; + Guid inventoryId = (Guid)args[0]; + Guid itemId = (Guid)args[1]; - if (!item.Tags.ContainsKey(tag)) + return SubscribeToItemEvent(inventoryId, itemId, fromScript.ID, "WANTS_ON_POPUP_ITEM_CLICK_EVENTS"); + } + case "UNSUBSCRIBE_FROM_ON_POPUP_ITEM_CLICK_EVENT_FOR_ITEM": + { + if (args == null) return false; - // Remove tag from item - item.Tags.Remove(tag); + Guid inventoryId = (Guid)args[0]; + Guid itemId = (Guid)args[1]; - return true; + return UnsubscribeFromItemEvent(inventoryId, itemId, "WANTS_ON_POPUP_ITEM_CLICK_EVENTS"); } } @@ -798,10 +1265,25 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri return null; } + private void Main_ProcessPad(UIntPtr padPtr) + { + //if (playerInventory.IsVisible) + //{ + // IVPad.FromUIntPtr(padPtr).Values[(int)ePadControls.INPUT_ATTACK].CurrentValue = 0; + //} + } + private void Main_Tick(object sender, EventArgs e) { if (ModSettings.DisableInMP && IVNetwork.IsNetworkGameRunning()) return; + + // Process queue + while (mainThreadQueue.Count != 0) + mainThreadQueue.Dequeue()?.Invoke(); + + // Display custom tutorials + DisplayCustomMissionIntroductionText(); // Get player stuff IVPed playerPed = IVPed.FromUIntPtr(IVPlayerInfo.FindThePlayerPed()); @@ -813,7 +1295,7 @@ private void Main_Tick(object sender, EventArgs e) // Handle inventory open/closing and quick weapon switch to last weapon or fist bool wantsToOpenViaController = NativeControls.IsUsingJoypad() && NativeControls.IsControllerButtonPressed(0, ControllerButton.BUTTON_BUMPER_LEFT); - if (IsKeyPressed(Keys.Tab) || wantsToOpenViaController) + if ((IsKeyPressed(Keys.Tab) || wantsToOpenViaController) && !playerPed.IsInVehicle()) { if (inventoryKeyWatch.IsRunning) { @@ -825,6 +1307,7 @@ private void Main_Tick(object sender, EventArgs e) { GET_SCREEN_RESOLUTION(out int x, out int y); SetCursorPos(x / 2, y / 2); + wasCursorPosSet = true; } @@ -899,8 +1382,11 @@ private void Main_Tick(object sender, EventArgs e) } // Handle some other stuff + ResizeInventoryBasedOnClothes(); + CheckForInvalidScriptItems(); HandleTimescaleInterpolation(); HandlePreventPlayerToCollectPickup(); + HandlePlayerImpacts(playerPed); // Update all inventory items BasicInventoryItem[] items = playerInventory.GetItems(); @@ -993,12 +1479,22 @@ private void Main_Tick(object sender, EventArgs e) item.Tags.Add("IS_GAME_WEAPON", null); item.Tags.Add("WeaponType", weaponType); + string weaponName = NativeGame.GetCommonWeaponName(type); + item.PopupMenuItems.Add("Drop"); - item.TopLeftText = NativeGame.GetCommonWeaponName(type); + item.TopLeftText = weaponName; if (loadedWeaponTextures.ContainsKey(weaponType)) item.Icon = loadedWeaponTextures[weaponType]; + if (predefinedItemWeights.ContainsKey(weaponName)) + { + float weight = predefinedItemWeights[weaponName]; + item.Tags.Add("WEIGHT", weight); + item.BottomRightText = weight.ToString() + "lb"; + item.BottomRightColor = Color.FromArgb(100, item.BottomRightColor); + } + playerInventory.AddItem(item); } } diff --git a/PackAndCarry/PackAndCarry/PackAndCarry.csproj b/PackAndCarry/PackAndCarry/PackAndCarry.csproj index 98e747e..98f625b 100644 --- a/PackAndCarry/PackAndCarry/PackAndCarry.csproj +++ b/PackAndCarry/PackAndCarry/PackAndCarry.csproj @@ -11,7 +11,7 @@ PackAndCarry.ivsdk v4.8 512 - true + false disable @@ -40,8 +40,8 @@ ..\..\..\CustomInventoryIV\CustomInventoryIV\bin\Debug\CustomInventoryIV.dll - - ..\packages\IVSDKDotNetWrapper.1.7.0\lib\net472\IVSDKDotNetWrapper.dll + + ..\packages\IVSDKDotNetWrapper.1.8.0\lib\net472\IVSDKDotNetWrapper.dll @@ -51,6 +51,7 @@ + diff --git a/PackAndCarry/PackAndCarry/Properties/AssemblyInfo.cs b/PackAndCarry/PackAndCarry/Properties/AssemblyInfo.cs index 5ce99f3..7c451ed 100644 --- a/PackAndCarry/PackAndCarry/Properties/AssemblyInfo.cs +++ b/PackAndCarry/PackAndCarry/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("ItsClonkAndre")] [assembly: AssemblyProduct("PackAndCarry")] -[assembly: AssemblyCopyright("Copyright © ItsClonkAndre 2024")] +[assembly: AssemblyCopyright("Copyright © ItsClonkAndre 2024-2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.0.0.*")] +//[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/PackAndCarry/PackAndCarry/packages.config b/PackAndCarry/PackAndCarry/packages.config index 0db1b56..3615eea 100644 --- a/PackAndCarry/PackAndCarry/packages.config +++ b/PackAndCarry/PackAndCarry/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file From a4fd09204e1ff8ae0ce4a2ba7122480a095b6b28 Mon Sep 17 00:00:00 2001 From: "E. S." <39125931+ClonkAndre@users.noreply.github.com> Date: Wed, 11 Dec 2024 23:33:43 +0100 Subject: [PATCH 3/5] Another update - Added new script commands. - Added event for when the inventory gets resized and a custom item is no longer able to fit in the inventory. - Some other minor code changes --- PackAndCarry/PACApiTest/Main.cs | 33 +++++++++-- PackAndCarry/PackAndCarry/Main.cs | 99 +++++++++++++++++++++++++++---- 2 files changed, 115 insertions(+), 17 deletions(-) diff --git a/PackAndCarry/PACApiTest/Main.cs b/PackAndCarry/PACApiTest/Main.cs index 7a70b06..8c9b515 100644 --- a/PackAndCarry/PACApiTest/Main.cs +++ b/PackAndCarry/PACApiTest/Main.cs @@ -44,7 +44,6 @@ public Main() ScriptCommandReceived += Main_ScriptCommandReceived; OnFirstD3D9Frame += Main_OnFirstD3D9Frame; OnImGuiRendering += Main_OnImGuiRendering; - Tick += Main_Tick; } #endregion @@ -89,6 +88,14 @@ private object Main_ScriptCommandReceived(Script fromScript, object[] args, stri IVGame.Console.PrintWarningEx("Received PAC_ON_POPUP_ITEM_CLICKED script command from inventory {0} for item {1}. Clicked on {2}", inventoryId, itemId, popupItemName); } break; + case "PAC_ON_ITEM_BEING_LEFT_BEHIND": + { + Guid inventoryId = (Guid)args[0]; + Guid itemId = (Guid)args[1]; + + IVGame.Console.PrintWarningEx("Received PAC_ON_ITEM_BEING_LEFT_BEHIND script command from inventory {0} for item {1}.", inventoryId, itemId); + } + break; } return null; @@ -120,6 +127,7 @@ private void Main_OnImGuiRendering(IntPtr devicePtr, ImGuiIV_DrawingContext ctx) if (PlayerInventoryID == Guid.Empty) { ImGuiIV.TextColored(Color.Yellow, "Get the player inventory id first to be able to interact with it"); + ImGuiIV.End(); return; } @@ -159,7 +167,8 @@ private void Main_OnImGuiRendering(IntPtr devicePtr, ImGuiIV_DrawingContext ctx) if (addedItemID == Guid.Empty) { - ImGuiIV.TextColored(Color.Yellow, "Add a new item to the player inventory first to be ablet to interact with it"); + ImGuiIV.TextColored(Color.Yellow, "Add a new item to the player inventory first to be able to interact with it"); + ImGuiIV.End(); return; } @@ -356,12 +365,24 @@ private void Main_OnImGuiRendering(IntPtr devicePtr, ImGuiIV_DrawingContext ctx) } } - ImGuiIV.End(); - } + ImGuiIV.Spacing(2); - private void Main_Tick(object sender, EventArgs e) - { + if (ImGuiIV.Button("SUBSCRIBE_TO_ON_ITEM_BEING_LEFT_BEHIND_EVENT_FOR_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "SUBSCRIBE_TO_ON_ITEM_BEING_LEFT_BEHIND_EVENT_FOR_ITEM", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("SUBSCRIBE_TO_ON_ITEM_BEING_LEFT_BEHIND_EVENT_FOR_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + if (ImGuiIV.Button("UNSUBSCRIBE_FROM_ON_ITEM_BEING_LEFT_BEHIND_EVENT_FOR_ITEM")) + { + if (SendScriptCommand("PackAndCarry", "UNSUBSCRIBE_FROM_ON_ITEM_BEING_LEFT_BEHIND_EVENT_FOR_ITEM", new object[] { PlayerInventoryID, addedItemID }, out object result)) + { + IVGame.Console.PrintEx("UNSUBSCRIBE_FROM_ON_ITEM_BEING_LEFT_BEHIND_EVENT_FOR_ITEM result: {0}", Convert.ToBoolean(result)); + } + } + ImGuiIV.End(); } } diff --git a/PackAndCarry/PackAndCarry/Main.cs b/PackAndCarry/PackAndCarry/Main.cs index 3a0ce45..dbd1d8b 100644 --- a/PackAndCarry/PackAndCarry/Main.cs +++ b/PackAndCarry/PackAndCarry/Main.cs @@ -601,9 +601,13 @@ private bool UnsubscribeFromItemEvent(Guid inventoryId, Guid itemId, string even #region Events private void BasicInventory_OnItemDraggedOut(BasicInventory sender, BasicInventoryItem item, int itemIndex) { - if (item.Tags.ContainsKey("IS_GAME_WEAPON")) + // Check if this is a custom item first before doing stuff with it + if (!item.Tags.ContainsKey("IS_CUSTOM_ITEM")) { - DropItem(sender, item); + if (item.Tags.ContainsKey("IS_GAME_WEAPON")) + { + DropItem(sender, item); + } } // Raise API "PAC_ON_ITEM_DRAGGED_OUT" event for subscriber @@ -620,11 +624,15 @@ private void BasicInventory_OnItemDraggedToNewSlot(BasicInventory sender, BasicI } private void BasicInventory_OnPopupItemClick(BasicInventory sender, BasicInventoryItem item, string popupItemName) { - if (item.Tags.ContainsKey("IS_GAME_WEAPON")) + // Check if this is a custom item first before doing stuff with it + if (!item.Tags.ContainsKey("IS_CUSTOM_ITEM")) { - if (popupItemName == "Drop") + if (item.Tags.ContainsKey("IS_GAME_WEAPON")) { - DropItem(sender, item); + if (popupItemName == "Drop") + { + DropItem(sender, item); + } } } @@ -634,11 +642,15 @@ private void BasicInventory_OnPopupItemClick(BasicInventory sender, BasicInvento } private void BasicInventory_OnItemClick(BasicInventory sender, BasicInventoryItem item, int itemIndex) { - if (item.Tags.ContainsKey("IS_GAME_WEAPON")) + // Check if this is a custom item first before doing stuff with it + if (!item.Tags.ContainsKey("IS_CUSTOM_ITEM")) { - int weaponType = Convert.ToInt32(item.Tags["WeaponType"]); - lastPlayerWeapon = weaponType; - SET_CURRENT_CHAR_WEAPON(playerPedHandle, weaponType, false); + if (item.Tags.ContainsKey("IS_GAME_WEAPON")) + { + int weaponType = Convert.ToInt32(item.Tags["WeaponType"]); + lastPlayerWeapon = weaponType; + SET_CURRENT_CHAR_WEAPON(playerPedHandle, weaponType, false); + } } // Raise API "PAC_ON_ITEM_CLICKED" event for subscriber @@ -658,6 +670,16 @@ private void BasicInventory_OnInventoryResized(BasicInventory target, List Date: Thu, 12 Dec 2024 02:09:04 +0100 Subject: [PATCH 4/5] Updated readme --- README.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a1d0f98..805c88d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,23 @@ # Pack & Carry -This is a mod which adds a custom, more advanced inventory to GTA IV using the [CustomInventoryIV](https://github.com/ClonkAndre/CustomInventoryIV) library. +Pack & Carry is a mod which adds a custom, more advanced inventory to GTA IV using the [CustomInventoryIV](https://github.com/ClonkAndre/CustomInventoryIV) library. -Work in progress. +## Features +- **Dynamic Inventory Size**: Inventory space adjusts based on equipped gear like backpacks, bulletproof vests, and duffle bags. +- **Weight Mechanics**: Carrying heavy loads affects player movement, adding realism. +- **Controller & Keyboard Support**: Navigate the inventory easily with both input methods. +- **Customization**: Most of the features this mod has to offer are easily customizable. +- **Modder-Friendly Integration**: Other mods can add custom items to the inventory by using the script commands of this mod. + +## Requirements +- [IV-SDK .NET](https://github.com/ClonkAndre/IV-SDK-DotNet) +- [ClonksCodingLib.GTAIV](https://github.com/ClonkAndre/ClonksCodingLib.GTAIV) + +## Other Links +GTAForums: TODO +GTAInside: TODO + +## Available Script Commands +Check out the Pack & Carry [Wiki](https://github.com/ClonkAndre/PackAndCarry/wiki) to find out which script commands there are, and how you can use them. + +## How to Contribute +Do you have an idea to improve this mod, or did you happen to run into a bug? Please share your idea or the bug you found in the [issues](https://github.com/ClonkAndre/PackAndCarry/issues) page, or even better: feel free to fork and contribute to this project with a [Pull Request](https://github.com/ClonkAndre/PackAndCarry/pulls). From a10329e62160efed717c038794650bbf70e56b95 Mon Sep 17 00:00:00 2001 From: "E. S." <39125931+ClonkAndre@users.noreply.github.com> Date: Fri, 13 Dec 2024 08:22:10 +0100 Subject: [PATCH 5/5] Update readme - Added credits section --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 805c88d..159413a 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,9 @@ Pack & Carry is a mod which adds a custom, more advanced inventory to GTA IV usi - [IV-SDK .NET](https://github.com/ClonkAndre/IV-SDK-DotNet) - [ClonksCodingLib.GTAIV](https://github.com/ClonkAndre/ClonksCodingLib.GTAIV) +## Credits +[HD Weapon icons by RollY](https://gamebanana.com/mods/27174) + ## Other Links GTAForums: TODO GTAInside: TODO