Pokered Save Editor 2
Pokemon Red & Blue save file editor - Qt 6 C++/QML
Loading...
Searching...
No Matches
itemmarketentry.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 Twilight
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15*/
16
21
22#include <QDebug>
23
24#include "./itemmarketentry.h"
25#include "../itemmarketmodel.h"
27
34
36{
37 // Remove all pointers in array, don't delete them as they are not owned by
38 // this class
39 auto instArr = instances.value(whichType());
40 instArr->removeAll(this);
41
42 // Delete whole instances array if it's empty of that type
43 if(instArr->empty()) {
44 delete instances.value(whichType());
45 instances.remove(whichType());
46 }
47
48 // Remove all instances combined, again not deleting them because they are not
49 // owned by this class
50 instancesCombined.removeAll(this);
51}
52
54{
55 //
56}
57
59{
60 if(!instances.contains(whichType())) {
61 instances.insert(whichType(), new QVector<ItemMarketEntry*>());
62 initOnce();
63 }
64
65 auto instArr = instances.value(whichType());
66 instArr->append(this);
67
68 instancesCombined.append(this);
69}
70
72{
73 if(!cache.contains(HashKeyName))
74 cache.insert(HashKeyName, _name());
75
76 return cache.value(HashKeyName).toString();
77}
78
80{
81 if(!cache.contains(HashKeyInStockCount))
83
84 return cache.value(HashKeyInStockCount).toInt();
85}
86
88{
89 if(!cache.contains(HashKeyCanSell))
90 cache.insert(HashKeyCanSell, _canSell());
91
92 return cache.value(HashKeyCanSell).toBool();
93}
94
96{
97 if(!cache.contains(HashKeyItemWorth))
99
100 return cache.value(HashKeyItemWorth).toInt();
101}
102
104{
105 // LATENT-UB NOTE (flagged for review — see notes/plans/testing.md): the base
106 // ~ItemMarketEntry() destructor calls whichType() for cleanup. If whichType()
107 // was ALREADY called during the object's life (the normal case — the model uses
108 // it for grouping/filtering), the destructor hits the cache below and never
109 // reaches _whichType(), so it's safe. But if an entry were destroyed WITHOUT
110 // whichType() ever being called, _whichType() (pure virtual in this base) would
111 // be invoked after the derived part is gone == undefined behavior. clang-tidy
112 // (clang-analyzer-cplusplus.PureVirtualCall) correctly flags the latent path.
113 // A real fix means caching the type as a plain member set by the derived class
114 // (the ctor can't call it either) -- a lifetime refactor of this UAF-historied
115 // area, deferred to Twilight rather than changed unilaterally. Suppressed here
116 // because the cached-by-then invariant holds in every real code path.
117 if(!cache.contains(HashKeyWhichType))
118 // NOLINTNEXTLINE(clang-analyzer-cplusplus.PureVirtualCall)
120
121 return cache.value(HashKeyWhichType).toString();
122}
123
125{
126 // If compatibility is both either then it's auto true
128 return true;
129
130 // Here we do a hack, compat values line up with bool values.
131 // CompatNo = 0 or false and CompatYes = 1 or true
132 // Compare those bool values and check they match the target
133 bool _isMoneyCurrency = ((bool)compatMoneyCurrency) == (*isMoneyCurrency);
134 bool _isBuyMode = ((bool)compatBuyMode) == (*isBuyMode);
135
136 // Now if either one was CompatEither, they would have yielded incorrect
137 // results. We auto true any that is compat either.
139 _isMoneyCurrency = true;
140
142 _isBuyMode = true;
143
144 // After all is said and done, we simply check both are true meaning this
145 // request passes the filter
146 return _isMoneyCurrency && _isBuyMode;
147}
148
150{
151 return onCart;
152}
153
155{
156 if(!requestFilter())
157 return 0;
158
159 return itemWorth() * onCart;
160}
161
163{
164 int ret = 0;
165
166 if(activeList == nullptr)
167 return 0;
168
169 // Only same-type rows in the CURRENT list (was: the cross-app `instances` registry).
170 const QString myType = whichType();
171 for(auto el : *activeList) {
172 if(el->whichType() == myType)
173 ret += el->stackCount();
174 }
175
176 return ret;
177}
178
179// Signed net worth of the whole cart in its single currency: sells add, buys
180// subtract. (Money/exchange rows are excluded -- the exchange is its own mode.)
181// Uses the plain cartSignVal member, NOT a virtual, so sweeping a registry that may
182// hold a torn-down entry can't deref a freed vtable.
184{
185 int ret = 0;
186
187 if(activeList == nullptr)
188 return 0;
189
190 for(auto el : *activeList) {
191 if(el->exclude)
192 continue;
193
194 ret += el->cartSignVal * el->cartWorth();
195 }
196
197 return ret;
198}
199
200// Balance after the whole cart: starting balance in the active currency plus the
201// signed net (sells already +, buys already -). One formula for a mixed cart.
203{
204 const int start = (*isMoneyCurrency) ? (int)player->money : (int)player->coins;
205 return start + totalWorth();
206}
207
209{
210 if(!requestFilter())
211 return false;
212
213 // Allow only if there is space left and the total worth doesn't go above
214 // the players money/coins
215 return (onCart > 0) && (onCartLeft() >= 0) && (moneyLeftover() >= 0);
216}
217
219{
220 if(activeList == nullptr)
221 return false;
222
223 for(auto el : *activeList) {
224 if(el->canCheckout())
225 return true;
226 }
227
228 return false;
229}
230
232{
233 onCart = val;
234
235 if(onCart < 0)
236 onCart = 0;
237
238 onCartChanged();
239}
240
251
252bool* ItemMarketEntry::isBuyMode = nullptr;
255
256QHash<QString, QVector<ItemMarketEntry*>*> ItemMarketEntry::instances =
257 QHash<QString, QVector<ItemMarketEntry*>*>();
258
259QVector<ItemMarketEntry*> ItemMarketEntry::instancesCombined =
260 QVector<ItemMarketEntry*>();
261
262QVector<ItemMarketEntry*>* ItemMarketEntry::activeList = nullptr;
virtual QString _name()=0
Subtype: compute the display name.
ItemMarketEntry(int compatMoneyCurrency=CompatEither, int compatBuyMode=CompatEither)
int totalStackCount()
Stacks across all rows of this type.
int moneyLeftover()
Money remaining if this checks out.
static QVector< ItemMarketEntry * > * activeList
Current model's live rows.
void reUpdateConstants()
Clear the cached mode-stable values.
bool requestFilter()
Helper: does this row pass the current mode filter?
QString name()
Cached display name.
virtual QString _whichType()=0
Subtype: report the type label.
static QHash< QString, QVector< ItemMarketEntry * > * > instances
All rows, grouped by type.
int compatBuyMode
Buy/sell compatibility.
QString whichType()
Cached type label.
static bool * isMoneyCurrency
Shared: current currency mode.
int itemWorth()
Cached unit value.
static PlayerBasics * player
Shared: player money/coins.
QHash< int, QVariant > cache
Memoised mode-stable values (see HashKey* enum).
void setCartCount(int val)
Set the cart quantity (backs onCart).
int totalWorth()
Signed worth across ALL rows (sell +, buy -).
void finishConstruction()
Finalise construction (register the instance).
int inStockCount()
Cached owned/sellable count.
virtual int _itemWorth()=0
Subtype: compute the unit value.
virtual int _inStockCount()=0
Subtype: compute the owned/sellable count.
static QVector< ItemMarketEntry * > instancesCombined
All rows, flat.
virtual ~ItemMarketEntry()
virtual bool _canSell()=0
Subtype: compute sellability.
void doReUpdateConstants()
Force a refresh of the "mode-stable" values.
int onCart
Backing cart quantity.
int compatMoneyCurrency
Money/coins compatibility.
virtual void initOnce()
One-time setup for the first instance of a type.
bool canAnyCheckout()
Can any row check out?
int cartWorth()
Value of the cart quantity.
static bool * isBuyMode
Shared: current buy/sell mode.
bool canSell()
Cached sellable flag.
int getCartCount()
Current cart quantity (backs onCart).
virtual int onCartLeft()=0
Subtype: how many more may be added.
virtual bool canCheckout()
Can this row alone check out?
The trainer's headline values: name, ID, money, coins, badges, starter.