Repository navigation
Expand file tree
/
Copy pathCount.cpp
More file actions
117 lines (97 loc) · 4.37 KB
/
Copy pathCount.cpp
File metadata and controls
117 lines (97 loc) · 4.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// This file is part of ClassicAPI.
//
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
// of the GNU General Public License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
// `C_Item.GetItemCount(itemInfo [, includeBank [, includeUses]])` — a
// straight port of 3.3.5's `Script_GetItemCount` (0x0051C2E0; 4.3.4's is
// identical) onto 1.12's own inventory searches, which are the ancestors of
// 3.3.5's `CGBag_C::FindItem` family with the same flag bits:
//
// total = GetItemTypeCount(itemID, includeBank ? BANK : 0)
// → FUN_INVMGR_COUNT_STACKS_BY_ID
// includeUses, for a non-stackable item whose base charges are negative:
// total = GetItemTypeCharges(itemID, 0) → FUN_INVMGR_COUNT_CHARGES_BY_ID
// and if the use spell's reagents allow N casts, total = N.
//
// So the count is the engine's: equipment, the equipped bags themselves,
// backpack, keyring and bag contents (plus the bank, read without a banker),
// broken items skipped. 1.12's walk has no 3.3.5 IGNORE_ITEM_STATE (0x80)
// bit, so includeUses can't count broken items the way 3.3.5 does. 3.3.5's
// use-enchant branch (`FindUseEnchantItemPtr`) is not ported; base charges
// and the base use spell stand for it.
#include "item/Count.h"
#include "Game.h"
#include "Offsets.h"
#include "item/Location.h"
#include "item/Record.h"
#include "spell/CastCount.h"
#include "unit/Identity.h"
#include <cstdint>
namespace Item::Count {
namespace {
using CountByID_t = int(__thiscall *)(const void *invMgr, int itemID, uint32_t flags);
int CountByID(uintptr_t fn, int itemID, uint32_t flags) {
const uint8_t *invMgr = Unit::Identity::PlayerInventoryManager();
if (invMgr == nullptr)
return 0;
return reinterpret_cast<CountByID_t>(fn)(invMgr, itemID, flags);
}
// 3.3.5's `CGItem_C::NumBaseCharges` / `GetUseSpell`: the first on-use
// spell slot's charges and spell.
struct UseSpell {
int spellID;
int charges;
};
UseSpell FirstUseSpell(const uint8_t *record) {
for (int i = 0; i < Offsets::ITEMSTATS_SPELL_SLOT_COUNT; ++i) {
const int spellID = Game::Read<int>(record, Offsets::OFF_ITEMSTATS_SPELL_ID + i * 4);
const uint32_t trigger =
Game::Read<uint32_t>(record, Offsets::OFF_ITEMSTATS_SPELL_TRIGGER + i * 4);
if (spellID != 0 && trigger == Offsets::ITEM_SPELLTRIGGER_ON_USE)
return {spellID, Game::Read<int>(record, Offsets::OFF_ITEMSTATS_SPELL_CHARGES + i * 4)};
}
return {0, 0};
}
int CountUses(int itemID, int total) {
const uint8_t *record = Item::PeekRecord(static_cast<uint32_t>(itemID));
if (record == nullptr || Game::Read<int>(record, Offsets::OFF_ITEMSTATS_STACKABLE) > 1)
return total;
const UseSpell use = FirstUseSpell(record);
if (use.charges < 0)
total = CountByID(Offsets::FUN_INVMGR_COUNT_CHARGES_BY_ID, itemID, 0);
if (const int casts = ::Spell::CastCount::ForSpell(use.spellID))
total = casts;
return total;
}
int __fastcall Script_C_Item_GetItemCount(void *L) {
const bool includeBank = Game::Lua::ToFlag(L, 2);
const bool includeUses = Game::Lua::ToFlag(L, 3);
const uint32_t flags = includeBank ? Offsets::INVMGR_SEARCH_BANK : 0;
const int itemID = Item::Location::IDForArg(L, 1, flags);
int total = itemID > 0 ? CountByID(Offsets::FUN_INVMGR_COUNT_STACKS_BY_ID, itemID, flags) : 0;
if (total != 0 && includeUses)
total = CountUses(itemID, total);
Game::Lua::SetTop(L, 0);
Game::Lua::PushNumber(L, static_cast<double>(total));
return 1;
}
} // namespace
int InInventory(int itemID) {
if (itemID <= 0)
return 0;
return CountByID(Offsets::FUN_INVMGR_COUNT_STACKS_BY_ID, itemID, 0);
}
static void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_Item", "GetItemCount",
&Script_C_Item_GetItemCount);
}
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
} // namespace Item::Count