Skip to content

Commit d0b6a7c

Browse files
committed
C_Container.GetContainerItemCharges
1 parent 96b0cf6 commit d0b6a7c

5 files changed

Lines changed: 196 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Full per-function reference: **[docs/API.md](docs/API.md)**.
2323
| [Class](docs/API.md#class) | `FillLocalizedClassList` |
2424
| [Combat](docs/API.md#combat) | `InCombatLockdown` |
2525
| [CVar](docs/API.md#cvar) | `C_CVar.GetCVarBool` |
26-
| [Container](docs/API.md#container) | `C_Container.GetContainerItemDurability`, `C_Container.GetContainerItemID`, `C_Container.GetContainerItemRepairCost`, `C_Container.GetContainerNumFreeSlots`, `C_Container.GetItemCooldown`, `C_Container.MoveItem`, `C_Container.PlayerHasHearthstone`, `C_Container.SwapItems`, `C_Container.UseHearthstone`, `GetItemCooldown` |
26+
| [Container](docs/API.md#container) | `C_Container.GetContainerItemCharges`, `C_Container.GetContainerItemDurability`, `C_Container.GetContainerItemID`, `C_Container.GetContainerItemRepairCost`, `C_Container.GetContainerNumFreeSlots`, `C_Container.GetItemCooldown`, `C_Container.MoveItem`, `C_Container.PlayerHasHearthstone`, `C_Container.SwapItems`, `C_Container.UseHearthstone`, `GetItemCooldown` |
2727
| EncodingUtil | `C_EncodingUtil.CompressString`, `C_EncodingUtil.DecompressString`, `C_EncodingUtil.EncodeBase64`, `C_EncodingUtil.DecodeBase64`, `C_EncodingUtil.EncodeHex`, `C_EncodingUtil.DecodeHex`, `C_EncodingUtil.SerializeJSON`, `C_EncodingUtil.DeserializeJSON`, `C_EncodingUtil.SerializeCBOR`, `C_EncodingUtil.DeserializeCBOR` |
2828
| [EquipmentSet](docs/API.md#equipmentset) | `C_EquipmentSet.CanUseEquipmentSets`, `C_EquipmentSet.ClearIgnoredSlotsForSave`, `C_EquipmentSet.CreateEquipmentSet`, `C_EquipmentSet.DeleteEquipmentSet`, `C_EquipmentSet.EquipmentSetContainsLockedItems`, `C_EquipmentSet.GetEquipmentSetID`, `C_EquipmentSet.GetEquipmentSetIDs`, `C_EquipmentSet.GetEquipmentSetInfo`, `C_EquipmentSet.GetIgnoredSlots`, `C_EquipmentSet.GetItemIDs`, `C_EquipmentSet.GetItemLocations`, `C_EquipmentSet.GetNumEquipmentSets`, `C_EquipmentSet.IgnoreSlotForSave`, `C_EquipmentSet.IsSlotIgnoredForSave`, `C_EquipmentSet.ModifyEquipmentSet`, `C_EquipmentSet.SaveEquipmentSet`, `C_EquipmentSet.UnignoreSlotForSave`, `C_EquipmentSet.UseEquipmentSet` (sets persist to `WTF\Account\...\ClassicAPI_EquipmentSets.txt`) |
2929
| [Events](docs/API.md#events) | `C_EventUtils.IsEventValid` |

docs/API.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ build instructions.
3434
- [`GetItemCooldown(itemInfo)` / `C_Container.GetItemCooldown(itemID)`](#getitemcooldowniteminfo--c_containergetitemcooldownitemid)
3535
- [`C_Container.GetContainerItemDurability(containerIndex, slotIndex)`](#c_containergetcontaineritemdurabilitycontainerindex-slotindex)
3636
- [`C_Container.GetContainerItemRepairCost(containerIndex, slotIndex)`](#c_containergetcontaineritemrepaircostcontainerindex-slotindex)
37+
- [`C_Container.GetContainerItemCharges(containerIndex, slotIndex)`](#c_containergetcontaineritemchargescontainerindex-slotindex)
3738
- [`C_Container.GetContainerNumFreeSlots(bagID)`](#c_containergetcontainernumfreeslotsbagid)
3839
- [`C_Container.PlayerHasHearthstone()`](#c_containerplayerhashearthstone)
3940
- [`C_Container.UseHearthstone()`](#c_containerusehearthstone)
@@ -770,6 +771,62 @@ Useful for "repair only items above N copper" smart-repair logic
770771
without scanning tooltips. ClassicAPI addition; modern WoW has no
771772
direct equivalent.
772773

774+
### `C_Container.GetContainerItemCharges(containerIndex, slotIndex)`
775+
776+
Per-slot equivalent of
777+
[`C_Item.GetItemCount`](#c_itemgetitemcountitem-includebank-includecharges)'s
778+
`includeCharges=true` mode — returns the total uses available in
779+
*this single slot*, where `GetItemCount` totals the same value
780+
across every matching slot.
781+
782+
```
783+
uses = C_Container.GetContainerItemCharges(containerIndex, slotIndex)
784+
```
785+
786+
`containerIndex` accepts the same values as
787+
[`C_Container.GetContainerItemDurability`](#c_containergetcontaineritemdurabilitycontainerindex-slotindex)
788+
(`0` = backpack, `1..4` = equipped bags, `-1`/`5..11` = bank, etc.).
789+
`slotIndex` is 1-based.
790+
791+
Math is `stack * usesPerItem`, where `usesPerItem` is
792+
`abs(SPELL_CHARGES[0])` for items with a negative charges field
793+
(consume-on-use: wands, healthstones, mana gems, sapper charges)
794+
and `1` for everything else. Worked examples:
795+
796+
| Slot contents | Stack | Raw charges | Returns |
797+
|---|---:|---:|---:|
798+
| Wand of Decay at 50 charges | 1 | -50 | **50** |
799+
| Wand at 1 charge | 1 | -1 | **1** |
800+
| Healthstone | 1 | -1 | **1** |
801+
| Stack of 20 water | 20 | -1 | **20** |
802+
| Stack of 5 mana potions | 5 | -1 | **5** |
803+
| Hearthstone (cooldown-only) | 1 | 0 | **1** |
804+
| Any other item in the slot | 1 | 0 | **1** |
805+
806+
Returns `nil` only when the slot is empty.
807+
808+
```lua
809+
-- "how many charges does my wand have left?"
810+
local charges = C_Container.GetContainerItemCharges(0, 1)
811+
print("Wand charges remaining: " .. charges)
812+
813+
-- "how many drinks across the bags?" — same as GetItemCount(water, false, true)
814+
local total = 0
815+
for bag = 0, 4 do
816+
for slot = 1, GetContainerNumSlots(bag) do
817+
if C_Container.GetContainerItemID(bag, slot) == waterID then
818+
total = total + C_Container.GetContainerItemCharges(bag, slot)
819+
end
820+
end
821+
end
822+
```
823+
824+
ClassicAPI addition; modern WoW has no direct equivalent (modern
825+
addons read charges off tooltip text). Useful when you need a
826+
per-slot number rather than `GetItemCount`'s rollup — e.g. to
827+
display "X charges" on the slot UI for a wand without re-walking
828+
every other matching item.
829+
773830
### `C_Container.GetContainerNumFreeSlots(bagID)`
774831

775832
Returns the number of empty slots in the given bag, plus the bag's

src/container/Charges.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// This file is part of ClassicAPI.
2+
//
3+
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
4+
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
5+
// version 3 of the License, or (at your option) any later version.
6+
//
7+
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
8+
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9+
// PURPOSE. See the GNU Lesser General Public License for more details.
10+
//
11+
// You should have received a copy of the GNU Lesser General Public License along with
12+
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
13+
14+
// `C_Container.GetContainerItemCharges(containerIndex, slotIndex)` —
15+
// returns the `abs(SPELL_CHARGES[0])` value of the item in the
16+
// specified bag slot, or `nil` for empty slots and items that don't
17+
// carry a charge concept. Wands return their remaining cast count
18+
// (e.g. 50 → 49 → …); single-use items (Healthstone, Mana Gem,
19+
// Goblin Sapper Charge) return `1`.
20+
//
21+
// Modern equivalent: none — modern WoW exposes charges only through
22+
// tooltip text scanning. `C_Item.GetItemCount(includeBank, includeCharges)`
23+
// totals charges across every matching stack; this is the per-slot
24+
// reader.
25+
26+
#include "Game.h"
27+
#include "item/Charges.h"
28+
#include "item/Location.h"
29+
30+
namespace Container::Charges {
31+
32+
namespace {
33+
34+
int __fastcall Script_C_Container_GetContainerItemCharges(void *L) {
35+
if (!Game::Lua::IsNumber(L, 1) || !Game::Lua::IsNumber(L, 2)) {
36+
Game::Lua::Error(L,
37+
"Usage: C_Container.GetContainerItemCharges(containerIndex, slotIndex)");
38+
return 0;
39+
}
40+
const int bagID = static_cast<int>(Game::Lua::ToNumber(L, 1));
41+
const int slotIndex = static_cast<int>(Game::Lua::ToNumber(L, 2));
42+
return Item::Charges::PushChargesForItem(
43+
L, Item::Location::ResolveBag(L, bagID, slotIndex));
44+
}
45+
46+
void RegisterLuaFunctions() {
47+
Game::Lua::RegisterTableFunction("C_Container", "GetContainerItemCharges",
48+
&Script_C_Container_GetContainerItemCharges);
49+
}
50+
51+
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
52+
53+
} // namespace
54+
55+
} // namespace Container::Charges

src/item/Charges.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// This file is part of ClassicAPI.
2+
//
3+
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
4+
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
5+
// version 3 of the License, or (at your option) any later version.
6+
//
7+
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
8+
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9+
// PURPOSE. See the GNU Lesser General Public License for more details.
10+
//
11+
// You should have received a copy of the GNU Lesser General Public License along with
12+
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
13+
14+
#include "Charges.h"
15+
16+
#include "../Game.h"
17+
#include "../Offsets.h"
18+
19+
#include <cstdlib>
20+
21+
namespace Item::Charges {
22+
23+
int PushChargesForItem(void *L, const uint8_t *item) {
24+
if (item == nullptr)
25+
return 0;
26+
auto *descriptor = *reinterpret_cast<const uint8_t *const *>(
27+
item + Offsets::OFF_ITEM_DESCRIPTOR);
28+
if (descriptor == nullptr)
29+
return 0;
30+
const int32_t stack = static_cast<int32_t>(*reinterpret_cast<const uint32_t *>(
31+
descriptor + Offsets::OFF_DESCRIPTOR_STACK_COUNT));
32+
const int32_t rawCharges = *reinterpret_cast<const int32_t *>(
33+
descriptor + Offsets::OFF_DESCRIPTOR_SPELL_CHARGES_0);
34+
const int32_t usesPerItem = (rawCharges < 0) ? -rawCharges : 1;
35+
Game::Lua::PushNumber(L, static_cast<double>(stack * usesPerItem));
36+
return 1;
37+
}
38+
39+
} // namespace Item::Charges

src/item/Charges.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// This file is part of ClassicAPI.
2+
//
3+
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
4+
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
5+
// version 3 of the License, or (at your option) any later version.
6+
//
7+
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
8+
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9+
// PURPOSE. See the GNU Lesser General Public License for more details.
10+
//
11+
// You should have received a copy of the GNU Lesser General Public License along with
12+
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
13+
14+
#pragma once
15+
16+
#include <cstdint>
17+
18+
namespace Item::Charges {
19+
20+
// Pushes the total uses available in this single slot — the
21+
// per-slot equivalent of `GetItemCount(_, _, includeCharges=true)`.
22+
// Returns `1` (pushed one value) on success, `0` (pushes nothing,
23+
// caller surfaces as `nil`) when the item is missing or the
24+
// descriptor is null.
25+
//
26+
// Math is `stack * usesPerItem` where `usesPerItem` follows
27+
// `Item::Count::GetUsesPerItem`:
28+
// - `SPELL_CHARGES[0] >= 0` → `1` (no destroy-on-zero behavior;
29+
// stack count alone reflects "how many uses").
30+
// - `SPELL_CHARGES[0] < 0` → `abs(value)` (consume-on-use items;
31+
// wand at 50 has `-50` so each item contributes 50).
32+
//
33+
// Worked examples:
34+
// - Wand at 50 charges (stack=1, raw=-50): `1 * 50 = 50`
35+
// - Hearthstone (stack=1, raw=0): `1 * 1 = 1` (cooldown-
36+
// only, no charges concept; we still surface "1 use").
37+
// - Water stack of 20 (stack=20, raw=-1): `20 * 1 = 20`
38+
// - Equipped sword (stack=1, raw=0): `1 * 1 = 1`
39+
//
40+
// Caller can `return PushChargesForItem(L, item);` to forward the
41+
// count directly.
42+
int PushChargesForItem(void *L, const uint8_t *item);
43+
44+
} // namespace Item::Charges

0 commit comments

Comments
 (0)