public static void DoDepositAll(Player player) { //this shouldn't happen if method is called correctly if (player.chest == -1) { return; } bool sendNetMsg = player.chest > -1; if (IHPlayer.ActionLocked(TIH.DepositAll)) { for (int i = R_START; i >= R_END; i--) { if (IHPlayer.SlotLocked(i) || player.inventory[i].IsBlank()) { continue; } MoveItemToChest(i, sendNetMsg); } Recipe.FindRecipes(); // !ref:Main:#22640.36# return; } for (int i = R_START; i >= R_END; i--) { if (!player.inventory[i].IsBlank()) { MoveItemToChest(i, sendNetMsg); } } Recipe.FindRecipes(); // !ref:Main:#22640.36# } //\DoDepositAll()
/// Locks/unlocks indicated slot depending on current status. public static void ToggleLock(Player player, int slotIndex) { if (slotIndex < 10 || slotIndex > 49) { return; } IHPlayer mp = player.GetSubClass <IHPlayer>(); mp.lockedSlots[slotIndex - 10] = !mp.lockedSlots[slotIndex - 10]; }
/// Only valid for the 40 Player inventory slots below the hotbar. /// <returns>True if indicated slot is locked</returns> public static bool SlotLocked(Player player, int slotIndex) { // pull IHPlayer subclass from the current Player-object's // list of subclasses IHPlayer mp = player.GetSubClass <IHPlayer>(); // subtract 10 since our array only contains 40 items and // we're ignoring the first 10 actual slots (the hotbar). return(slotIndex > 9 && slotIndex < 50 && mp.lockedSlots[slotIndex - 10]); }
public override void Initialize() { Instance = this; // MUST use "new", as tAPI derps with clearing (quoth: Miraimai) lockedSlots = new bool[40]; //not the hotbar // init "locked" status of all available actions; // not all actions are affected by this flag LockedActions = new Dictionary <TIH, bool>(); foreach (TIH aID in Enum.GetValues(typeof(TIH))) { LockedActions.Add(aID, false); } }
public override void PostDrawItemSlotBackground(SpriteBatch sb, ItemSlot slot) { if (IHBase.ModOptions["LockingEnabled"] && slot.type == "Inventory" && IHPlayer.SlotLocked(slot.index)) { sb.Draw(IHBase.LockedIcon, // the texture to draw slot.pos, // (Vector2) location in screen coords to draw sprite null, // Rectangle to specifies source texels from texture; null draws whole texture Color.Firebrick, // color to tint sprite; color.white=full color, no tint 0f, // angle in radians to rotate sprite around its center default(Vector2), // (Vector2) sprite origin, default=(0,0) i.e. upper left corner slot.scale, // (Vector2) scale factor SpriteEffects.None, // effects to apply 0f // layer depth; 0=front layer, 1=backlayer; SpriteSortMode can sort sprites ); } }
private void OnWorldLoad() { isLocked = IHPlayer.ActionLocked(Client.Action); if (isLocked) { Client.Hooks.PostDraw += PostDraw; // draw lock indicator // Client.Label = lockedLabel; } else { // List<>.Remove() doesn't fail on missing keys, // so this is safe Client.Hooks.PostDraw -= PostDraw; // Client.Label = initialLabel; } }
// Shift + Right Click on inventory slot toggles the lock state public override bool PreItemSlotRightClick(ItemSlot slot, ref bool release) { if (!KState.Special.Shift.Down()) { return(true); } if (IHBase.ModOptions["LockingEnabled"] && slot.modBase == null && Main.playerInventory && release) { if (slot.type == "Inventory" && slot.index >= 10) //not in the hotbar { IHPlayer.ToggleLock(slot.index); //toggle lock state Sound.Lock.Play(); } } return(false); }
/**************************************************** * This will compare the categories of items in the player's * inventory to those of items in the open container and * deposit any items of matching categories. */ public static void SmartDeposit() { if (Main.localPlayer.chest == -1) { return; } var pInventory = Main.localPlayer.inventory; //Item[] var chestItems = Main.localPlayer.chestItems; //Item[] var sendNetMsg = Main.localPlayer.chest > -1; //bool // define a query that creates category groups for the items in the chests, // then pulls out the category keys into a distinct list (List<ItemCat>) var catList = (from item in chestItems where !item.IsBlank() group item by item.GetCategory() into catGroup from cat in catGroup select catGroup.Key).Distinct() //no duplicates .ToList(); //store the query results if (IHBase.ModOptions["LockingEnabled"]) //slot locking on { for (int i = 49; i >= 10; i--) // reverse through player inv { if (!pInventory[i].IsBlank() && !IHPlayer.SlotLocked(i) && catList.Contains(pInventory[i].GetCategory())) { IHUtils.MoveItemToChest(i, sendNetMsg); } } } else //no locking { for (int i = 49; i >= 10; i--) { // if chest contains a matching category if (!pInventory[i].IsBlank() && catList.Contains(pInventory[i].GetCategory())) { IHUtils.MoveItemToChest(i, sendNetMsg); } } } Recipe.FindRecipes(); }
public static void DoQuickStack(Player player) { if (player.chest == -1) { return; } var inventory = player.inventory; var container = player.chestItems; bool sendMessage = player.chest > -1; var checkLocks = IHPlayer.ActionLocked(TIH.QuickStack); //boolean for (int iC = 0; iC < Chest.maxItems; iC++) // go through entire chest inventory. { //if chest item is not blank && not a full stack, then if (!container[iC].IsBlank() && container[iC].stack < container[iC].maxStack) { //for each item in inventory (including coins, ammo, hotbar), for (int iP = 0; iP < 58; iP++) { if (checkLocks && IHPlayer.SlotLocked(iP)) { continue; // if we're checking locks ignore the locked ones } if (container[iC].IsTheSameAs(inventory[iP])) //if chest item matches inv. item... { // RingBell(); //...play "item-moved" sound and... Sound.ItemMoved.Play(); // ...merge inv. item stack to chest item stack if (StackMerge(ref inventory[iP], container, iC)) { // do merge & check return (inv stack empty) status inventory[iP] = new Item(); // reset slot if all inv stack moved } else if (container[iC].IsBlank()) { // else, inv stack not empty after merge, but (because of DoCoins() call), // chest stack could be. container[iC] = inventory[iP].Clone(); // move inv item to chest slot inventory[iP] = new Item(); // and reset inv slot } // if (sendMessage) SendNetMessage(iC); //send net message if regular chest } } } } Recipe.FindRecipes(); // !ref:Main:#22640.36# }
protected override void AddButtonsToBases() { var bgColor = Constants.InvSlotColor * 0.8f; Func <TIH, string> getLabel = a => a.DefaultLabelForAction(true); Func <TIH, string> getTtip; if (IHBase.ModOptions["ShowTooltips"]) { getTtip = a => getLabel(a) + (IHBase.ModOptions["ShowKeyBind"] ? a.GetKeyTip() : ""); } else { getTtip = a => ""; } Func <TIH, TIH, TexturedButton> getButton = (base_by_action, a) => TexturedButton.New((ButtonSlot <TexturedButton>)ButtonBases[base_by_action], action: a, label: getLabel(a), tooltip: getTtip(a), bg_color: bgColor); // put buttons together var sort = getButton(TIH.Sort, TIH.Sort); var rsort = getButton(TIH.Sort, TIH.ReverseSort); var clean = getButton(TIH.CleanStacks, TIH.CleanStacks); // add services/actions sort.AddSortToggle(rsort); //for funsies let's just make that whole toggle thing pointless sort.Hooks.OnRightClick += () => IHPlayer.Sort(true); rsort.Hooks.OnRightClick += () => IHPlayer.Sort(); // TODO: make right-click throw all of the player's items on the ground. // Haha j/k. // Maybe. clean.EnableDefault(); }
private bool PreDraw(SpriteBatch sb) { // don't run unless there's a change to avoid adding/removing // the event every frame if (IHPlayer.ActionLocked(Client.Action) != isLocked) { isLocked = !isLocked; if (isLocked) { Client.Hooks.PostDraw += PostDraw; // Client.Label = lockedLabel; } else { Client.Hooks.PostDraw -= PostDraw; // Client.Label = initialLabel; } } return(true); }
/// <summary> /// Construct a list containing cloned copies of items in the given /// container, skipping blank (and optionally locked) slots. /// </summary> /// <param name="source_container">The Item[] array of the container</param> /// <param name="source_is_chest">Is the source container a chest? </param> /// <param name="range">Starting and ending indices defining the subset of the /// source's slots to be searched for items.</param> /// <returns> The new list of copied items, or null if no items were /// applicable to be copied (NOT an empty list!).</returns> public static List <Item> GetItemCopies(Item[] source_container, bool source_is_chest, Tuple <int, int> range = null) { if (range == null) { range = new Tuple <int, int>(0, source_container.Length - 1); } // initialize the list that will hold the copied items var itemList = new List <Item>(); int count = 0; //having trouble with empty lists... // get copies of viable items from container. // will need a different list if locking is enabled if (!source_is_chest && IHBase.ModOptions["LockingEnabled"]) { for (int i = range.Item1; i <= range.Item2; i++) { if (IHPlayer.SlotLocked(i) || source_container[i].IsBlank()) { continue; } itemList.Add(source_container[i].Clone()); count++; } } else //only skip blank slots { for (int i = range.Item1; i <= range.Item2; i++) { if (!source_container[i].IsBlank()) { itemList.Add(source_container[i].Clone()); } count++; } } // return null if no items were copied to new list return(count > 0 ? itemList : null); }
// // // // // // // // Makin buttons // // // // // // // private void addTextButtons() { // just feels right, man. var lockOffset = new Vector2(-20, -18); // get original or default label Func <TIH, string> getLabel = a => a.DefaultLabelForAction(true) + (IHBase.ModOptions["ShowKeyBind"] ? a.GetKeyTip() : ""); // put it all together, add to base Func <TIH, TIH, TextButton> getButton = (base_by_action, a) => TextButton.New((ButtonSlot <TextButton>)ButtonBases[base_by_action], action: a, label: getLabel(a) ); // putting these 2 on same base; see below var loot = getButton(TIH.LootAll, TIH.LootAll); var sort = getButton(TIH.LootAll, TIH.Sort); var depo = getButton(TIH.DepositAll, TIH.DepositAll); var sdep = getButton(TIH.DepositAll, TIH.SmartDeposit); var qstk = getButton(TIH.QuickStack, TIH.QuickStack); var sloo = getButton(TIH.QuickStack, TIH.SmartLoot); // * we're going to leave the vanilla buttons for these // var rena = getButton(TIH.Rename, TIH.Rename); // var save = getButton(TIH.Rename, TIH.SaveName); // here's an IDEA: Have LootAll toggle to Sort on shift. // Sort will reverse-sort on right-click. sort.EnableDefault().Hooks.OnRightClick += () => IHPlayer.Sort(true); loot.EnableDefault().AddToggle(sort); depo.EnableDefault().MakeLocking(lockOffset).AddToggle(sdep.EnableDefault()); qstk.EnableDefault().MakeLocking(lockOffset).AddToggle(sloo.EnableDefault()); }
public SortingToggleService(ICoreButton forward, ICoreButton reverse, KState.Special toggle_key) : base(forward, reverse, toggle_key) { sortAction = () => IHPlayer.Sort(); revSortAction = () => IHPlayer.Sort(true); }
static Constants() { LangInterIndices = new Dictionary <TIH, int> { { TIH.LootAll, 29 }, { TIH.DepositAll, 30 }, { TIH.QuickStack, 31 }, { TIH.Rename, 61 }, { TIH.SaveName, 47 }, { TIH.CancelEdit, 63 } }; DefaultButtonLabels = new Dictionary <TIH, string> { // Player Inventory { TIH.None, "" }, { TIH.Sort, "Sort" }, { TIH.ReverseSort, "Sort (Reverse)" }, { TIH.CleanStacks, "Clean Stacks" }, // Chests { TIH.SmartLoot, "Restock" }, { TIH.QuickStack, "Quick Stack" }, { TIH.SmartDeposit, "Smart Deposit" }, { TIH.DepositAll, "Deposit All" }, { TIH.LootAll, "Loot All" }, { TIH.Rename, "Rename" }, { TIH.SaveName, "Save" }, { TIH.CancelEdit, "Cancel" } }; DefaultClickActions = new Dictionary <TIH, Action> { { TIH.None, None }, // now unused. Remove? { TIH.Sort, () => IHPlayer.Sort() }, { TIH.ReverseSort, () => IHPlayer.Sort(true) }, { TIH.CleanStacks, IHPlayer.CleanStacks }, // Chest-only { TIH.SmartLoot, IHSmartStash.SmartLoot }, { TIH.QuickStack, IHUtils.DoQuickStack }, { TIH.SmartDeposit, IHSmartStash.SmartDeposit }, { TIH.DepositAll, IHUtils.DoDepositAll }, { TIH.LootAll, IHUtils.DoLootAll }, { TIH.Rename, EditChest.DoChestEdit }, { TIH.SaveName, EditChest.DoChestEdit }, { TIH.CancelEdit, EditChest.CancelRename } }; /************************************************ * Make getting a button's texture (texels) easier */ ButtonGridIndexByActionType = new Dictionary <TIH, int> { { TIH.Sort, 0 }, { TIH.ReverseSort, 1 }, { TIH.LootAll, 2 }, { TIH.DepositAll, 3 }, { TIH.SmartDeposit, 4 }, { TIH.CleanStacks, 5 }, { TIH.QuickStack, 6 }, { TIH.SmartLoot, 7 }, { TIH.Rename, 8 }, { TIH.SaveName, 8 } // this just varies by background color from Rename }; /************************************************* * Map action types to the string used for the corresponding * keybind in Modoptions.json */ ButtonActionToKeyBindOption = new Dictionary <TIH, string>() { { TIH.CleanStacks, "cleanStacks" }, { TIH.DepositAll, "depositAll" }, { TIH.SmartDeposit, "depositAll" }, { TIH.LootAll, "lootAll" }, { TIH.QuickStack, "quickStack" }, { TIH.SmartLoot, "quickStack" }, { TIH.Sort, "sort" }, { TIH.ReverseSort, "sort" }, // edit chest doesn't get a keyboard shortcut. So there. }; }
/// <returns>True if indicated action is set to respect locked slots.</returns> public static bool ActionLocked(Player player, TIH actionID) { IHPlayer mp = player.GetSubClass <IHPlayer>(); return(mp.LockedActions[actionID]); }
// TODO: Here are my thoughts on making sure shift-click syncs correctly with // the server: obviously, blindly placing the NetMessage update calls // wherever it seemed like Vanilla indicated they should go didn't work. // Now that I have a better understanding of what all that rigamarole is // supposed to do, I think that the most efficient way to handle this // would be to have any function that modifies a slot or slots in a // container record or return a list of the indices of those slots. // Numerous methods in IHUtils already return an int to indicate the // effect of their run, but only under certain conditions does that int // indicate the affected index. Other return statuses (statii?) could // indicate a modified slot or even multiple slots, but since I didn't // think I had any immediate need to know which slots those were, that // information was not recorded. // Now, it wouldn't be too hard to whip up a small data-transfer-object // that can be passed between method calls and hold both the return // status as it is now and a record of any slots which were modified // along the way; this would prevent us from having to take a // sledgehammer approach like we did with the calls that modify // chest-item-slot en-masse. While the sledge may be the best or // most-effective tool when many slots are likely to be modified at // once, this shift-click isn't likely to affect more than a few during // any one run, and looping over the entire container atleast twice for // each click is probably something we should avoid if we can. // Of course this is all obvious and I'm just being verbose for // absolute-clarity's sake. My point--and the catch--is that even // though the DTO would be simple to create and use, the code in IHUtils // is much more of a tangled mass of spaghetti-code than I realized, and // tracing down every spot where we'd need to make changes (though // likely small) to the code to integrate the object will be a delicate // job very prone to breaking things in unexpected ways. At least with // my luch it would be. Anyway, that's why, for now, I'm going to take // the sledgehammer to this and hope it doesn't affect performance too // badly. /// <summary> /// Shift + Left Click on item slot to move it between inventory and chest /// </summary> /// <param name="slot"> </param> /// <param name="release"> </param> /// <returns>True if shift not held while left-clicking, or if no applicable /// recipient is present to move item into. </returns> public override bool PreItemSlotLeftClick(ItemSlot slot, ref bool release) { if (!(bool)IHBase.Instance.options["enableShiftMove"].Value) { return(true); } if (slot.modBase == null && release && KState.Special.Shift.Down()) { if (Main.localPlayer.chestItems != null && !slot.MyItem.IsBlank()) //chests and banks { // Moving inventory item -> chest if (slot.type == "Inventory" || slot.type == "Coin" || slot.type == "Ammo") { IHPlayer.DoChestUpdateAction( () => { if (IHUtils.ShiftToChest(ref slot)) { Recipe.FindRecipes(); // !ref:Main:#22640.36# } }); } // Moving chest item -> inventory else if (slot.type == "Chest") { if (IHUtils.ShiftToPlayer(ref slot, Main.localPlayer.chest > -1)) { Recipe.FindRecipes(); // We can take the easy route here since there's only // one chest slot that could have been affected. if (Main.netMode == 1 && Main.localPlayer.chest > -1) //non-bank { IHUtils.SendNetMessage(slot.index); } } } return(false); } if (Main.craftGuide) //the Guide's crafting info slot { if (Main.guideItem.IsBlank() && !slot.MyItem.IsBlank() && (slot.type == "Inventory" || slot.type == "Coin" || slot.type == "Ammo")) { if (slot.MyItem.material && !slot.MyItem.notMaterial) { Sound.ItemMoved.Play(); Main.guideItem = slot.MyItem.Clone(); slot.MyItem = new Item(); Recipe.FindRecipes(); } } else if (!Main.guideItem.IsBlank() && slot.type == "CraftGuide") { if (IHUtils.ShiftToPlayer(ref slot, false)) { Main.guideItem = new Item(); } Recipe.FindRecipes(); } return(false); } if (Main.reforge) //Item reforging { if (Main.reforgeItem.IsBlank() && slot.type == "Inventory" && !slot.MyItem.IsBlank()) { if (slot.MyItem.maxStack == 1 && Prefix.CanHavePrefix(slot.MyItem)) { Sound.ItemMoved.Play(); Main.reforgeItem = slot.MyItem.Clone(); slot.MyItem = new Item(); Recipe.FindRecipes(); } } else if (!Main.reforgeItem.IsBlank() && slot.type == "Reforge") { if (IHUtils.ShiftToPlayer(ref slot, false)) { Main.reforgeItem = new Item(); } Recipe.FindRecipes(); } return(false); } } return(true); }
/// Set indicated action to respect/not-respect locked slots, /// depending on current status. public static void ToggleActionLock(Player p, TIH actionID) { IHPlayer mp = p.GetSubClass <IHPlayer>(); mp.LockedActions[actionID] = !mp.LockedActions[actionID]; }
private void addIconButtons() { // offset of lock indicator var lockOffset = new Vector2(-(float)(int)((float)Constants.ButtonW / 2) - 4, -(float)(int)((float)Constants.ButtonH / 2) + 8); Func <TIH, string> getLabel = a => Constants.DefaultButtonLabels[a]; Func <TIH, Color> getBGcol = (a) => (a == TIH.SaveName) ? Constants.EquipSlotColor * 0.85f : Constants.ChestSlotColor * 0.85f; Func <TIH, string> getTtip; if (IHBase.ModOptions["ShowTooltips"]) { if (IHBase.ModOptions["ShowKeyBind"]) { getTtip = a => getLabel(a) + IHUtils.GetKeyTip(a); } else { getTtip = a => getLabel(a); } } else { getTtip = a => ""; } Func <TIH, TIH, TexturedButton> getButton = (base_by_action, a) => TexturedButton.New((ButtonSlot <TexturedButton>)ButtonBases[base_by_action], action: a, label: getLabel(a), tooltip: getTtip(a), bg_color: getBGcol(a), texture: IHBase.ButtonGrid, inactive_rect: IHUtils.GetSourceRect(a), active_rect: IHUtils.GetSourceRect(a, true) ); // Btn obj Socket Action Button Action // ------- ------------- ------------- var sort = getButton(TIH.Sort, TIH.Sort); var rsort = getButton(TIH.Sort, TIH.ReverseSort); var loot = getButton(TIH.LootAll, TIH.LootAll); var depo = getButton(TIH.DepositAll, TIH.DepositAll); var sdep = getButton(TIH.DepositAll, TIH.SmartDeposit); var qstk = getButton(TIH.QuickStack, TIH.QuickStack); var sloo = getButton(TIH.QuickStack, TIH.SmartLoot); var rena = getButton(TIH.Rename, TIH.Rename); var save = getButton(TIH.Rename, TIH.SaveName); var cancel = TextButton.New(CancelEditBase, TIH.CancelEdit, getLabel(TIH.CancelEdit)); // Add Services // // sort enables default action for sort/rsort by ... default. sort.AddSortToggle(rsort); //for funsies let's just make that whole toggle thing pointless sort.Hooks.OnRightClick += () => IHPlayer.Sort(true); rsort.Hooks.OnRightClick += () => IHPlayer.Sort(); // add default click, let rClick lock it, and make shift switch buttons depo.EnableDefault().MakeLocking(lockOffset, Color.Firebrick).AddToggle(sdep.EnableDefault()); qstk.EnableDefault().MakeLocking(lockOffset, Color.Firebrick).AddToggle(sloo.EnableDefault()); // these just need their default actions enabled. loot.EnableDefault().Hooks.OnRightClick += () => IHPlayer.CleanStacks(); //why not cancel.EnableDefault(); // this prevents the "Cancel" text from being too big when the player // goes back into the rename interface (though it seems the vanilla // "Cancel" text behaves the same way...improvement!) cancel.Hooks.OnClick += CancelEditBase.ResetScale; // make Rename Chest button change to Save Name button when // clicked, and vice-versa. Well, technically, the buttons will // switch automatically when Main.editChest changes state, but // since that's what clicking these buttons does... save.EnableDefault().AddDynamicToggle(rena.EnableDefault(), () => Main.editChest); }
public static void Sort(Item[] container, bool chest, bool reverse, Tuple <int, int> range = null) { // if range param not specified, set it to whole container if (range == null) { range = new Tuple <int, int>(0, container.Length - 1); } // for clarity var checkLocks = IHBase.ModOptions["LockingEnabled"]; //boolean // get copies of the items and send them off to be sorted var sortedItemList = OrganizeItems(GetItemCopies(container, chest, range)); if (sortedItemList == null) { return; } if (reverse) { sortedItemList.Reverse(); //reverse on user request } // depending on user settings, decide if we copy items to end or beginning of container var fillFromEnd = chest ? IHBase.ModOptions["RearSortChest"] : IHBase.ModOptions["RearSortPlayer"]; //boolean // set up the functions that will be used in the iterators ahead Func <int, int> getIndex, getIter; Func <int, bool> getCond, getWhileCond; if (fillFromEnd) // use decrementing iterators { getIndex = x => range.Item2 - x; getIter = x => x - 1; getCond = x => x >= range.Item1; getWhileCond = x => x > range.Item1 && IHPlayer.SlotLocked(x); } else // use incrementing iterators { getIndex = y => range.Item1 + y; getIter = y => y + 1; getCond = y => y <= range.Item2; getWhileCond = y => y < range.Item2 && IHPlayer.SlotLocked(y); } int filled = 0; // num of slots filled (or locked) so far if (!chest && checkLocks) // player inv with locking enabled { // copy the sorted items back to the original container // (overwriting the current, unsorted contents) foreach (var item in sortedItemList) { // find the first unlocked slot. this would throw an // exception if range.Item1+filled somehow went over 49, but // if the categorizer and slot-locker are functioning // correctly, that _shouldn't_ be possible. Shouldn't. // Probably. while (IHPlayer.SlotLocked(getIndex(filled))) { filled++; } // now that we've found an unlocked slot, clone // the next sorted item into it. container[getIndex(filled++)] = item.Clone(); Sound.ItemMoved.Play(); } // and the rest of the slots should be empty for (int i = getIndex(filled); getCond(i); i = getIter(i)) { // find the first unlocked slot. if (IHPlayer.SlotLocked(i)) { continue; } container[i] = new Item(); } } else // just run through 'em all { foreach (var item in sortedItemList) { container[getIndex(filled++)] = item.Clone(); Sound.ItemMoved.Play(); } // and the rest of the slots should be empty for (int i = getIndex(filled); getCond(i); i = getIter(i)) { container[i] = new Item(); } } } // sort()
private void ToggleLock() { Sound.Lock.Play(); IHPlayer.ToggleActionLock(Client.Action); }