Pokered Save Editor 2
Pokemon Red & Blue save file editor - Qt 6 C++/QML
Loading...
Searching...
No Matches
itemstoragebox.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
22
23#include <algorithm>
24#include <QCollator>
25#include "../../qmlownership.h"
26
27#include "./itemstoragebox.h"
28#include "./item.h"
30#include "../../savefile.h"
33
34#include "../savefileexpanded.h"
35#include "../player/player.h"
36#include "../storage.h"
37
38#include <pse-db/itemsdb.h>
39#include <pse-common/random.h>
40
41ItemStorageBox::ItemStorageBox(bool isBag, int maxSize, SaveFile* saveFile, int offset)
44{
45 load(saveFile, offset);
46}
47
49{
50 for(auto item : items)
51 item->deleteLater();
52}
53
55{
56 return items.size();
57}
58
60{
61 int ret = 0;
62
63 for(auto el : items) {
64 ret += el->amount;
65 }
66
67 return ret;
68}
69
71{
72 return maxSize;
73}
74
76{
77 return isBag;
78}
79
81{
82 auto dest = destBox();
83
84 if(dest->items.size() >= dest->itemsMax())
85 return true;
86
87 return false;
88}
89
91{
92 if(ind >= items.size())
93 return nullptr;
94
95 return qmlCppOwned(items.at(ind));
96}
97
99{
100 // Between None and 3/4 of max capacity
101 var8 count = Random::inst()->rangeInclusive(0, maxSize * .75);
102
103 // Create that many random items
104 for(var8 i = 0; i < count; i++) {
105 itemNew();
106 }
107}
108
110{
111 // Essentials
112 items.append(new Item("TOWN MAP", 1));
114
115 items.append(new Item("POKE BALL", Random::inst()->rangeInclusive(5, 15)));
117
118 items.append(new Item("POTION", Random::inst()->rangeInclusive(5, 10)));
120
121 items.append(new Item("ANTIDOTE", Random::inst()->rangeInclusive(1, 3)));
123
124 items.append(new Item("PARLYZ HEAL", Random::inst()->rangeInclusive(1, 3)));
126
127 items.append(new Item("AWAKENING", Random::inst()->rangeInclusive(1, 3)));
129
130 // Again I have no idea where this will drop you so prepare for an escape
131 // If need be, also because you only get 1 HM Slave that doesn't know dig.
132 // This is your dig.
133 items.append(new Item("ESCAPE ROPE", Random::inst()->rangeInclusive(1, 5)));
135
136 // 25% chance of having these items
137 bool giveSuperPotion = Random::inst()->chanceSuccess(25);
138 if(giveSuperPotion) {
139 items.append(new Item("SUPER POTION", 1));
141 }
142
143 bool giveGreatBall = Random::inst()->chanceSuccess(25);
144 if(giveGreatBall) {
145 items.append(new Item("GREAT BALL", 1));
147 }
148
149 // Up to 5 more completely random items
150 var8 count = Random::inst()->rangeInclusive(0, 5);
151
152 // Create that many random items
153 for(var8 i = 0; i < count; i++) {
154 itemNew();
155 }
156}
157
158bool ItemStorageBox::itemMove(int from, int to)
159{
160 if(items.size() <= 0 ||
161 from == to ||
162 from >= items.size() ||
163 from < 0 ||
164 to >= items.size() ||
165 to < 0)
166 return false;
167
168 // Grab and remove item
169 auto eFrom = items.at(from);
170 items.removeAt(from);
171
172 // Insert it elsewhere
173 items.insert(to, eFrom);
174
175 itemMoveChange(from, to);
176 itemsChanged();
177
178 return true;
179}
180
182{
183 if(items.size() <= 0 ||
184 ind < 0 ||
185 ind >= items.size())
186 return;
187
188 items.at(ind)->deleteLater();
189 items.removeAt(ind);
190 itemRemoveChange(ind);
191 itemsChanged();
192}
193
195{
196 for(auto el : items) {
197 if(el->ind == ind)
198 return true;
199 }
200
201 return false;
202}
203
205{
206 int total = 0;
207
208 // Sum every matching row's amount (a box may legitimately hold the same item
209 // in more than one row -- pre-existing duplicate save data is supported).
210 for(auto el : items) {
211 if(el->ind == ind)
212 total += el->amount;
213 }
214
215 return total;
216}
217
219{
220 // Build the candidate pool: every real (non-glitch, non-once) item that this
221 // box doesn't already hold. Uniqueness is per-list, so we only check our own
222 // items -- the paired box is irrelevant.
223 QVector<int> pool;
224 for(auto entry : ItemsDB::inst()->getStore()) {
225 if(entry->getGlitch() || entry->getOnce())
226 continue;
227
228 if(hasItemInd(entry->getInd()))
229 continue;
230
231 pool.append(entry->getInd());
232 }
233
234 // The box already holds one of every available item -- nothing unique is left.
235 if(pool.isEmpty())
236 return -1;
237
238 return pool.at(Random::inst()->rangeExclusive(0, pool.size()));
239}
240
242{
243 if(items.size() >= maxSize)
244 return;
245
246 int ind = randomUniqueInd();
247
248 // Don't add a duplicate: if every available item is already here, do nothing.
249 if(ind < 0)
250 return;
251
252 // Amount range matches Item::randomize() (1-5).
253 items.append(new Item(static_cast<var8>(ind), Random::inst()->rangeInclusive(1, 5)));
255 itemsChanged();
256}
257
259{
260 auto dest = destBox();
261
262 bool ret = true;
263
264 while(items.size() > 0 && dest->items.size() < dest->itemsMax()) {
265 if(!relocateOne(0))
266 ret = false;
267 }
268
269 return ret;
270}
271
273{
274 auto dest = destBox();
275
276 if(items.size() <= 0 ||
277 ind < 0 ||
278 ind >= items.size() ||
279 dest->items.size() >= dest->itemsMax())
280 return false;
281
282 auto el = items.at(ind);
283 beforeItemRelocate(el);
284
285 items.removeAt(ind);
286 itemRemoveChange(ind);
287 itemsChanged();
288
289 dest->items.append(el);
290 dest->itemInsertChange();
291 dest->itemsChanged();
292
293 return true;
294}
295
297{
298 if(items.size() <= 0)
299 return;
300
301 // Setup Collator
302 QCollator collator;
303 collator.setNumericMode(true);
304 collator.setIgnorePunctuation(true);
305
306 std::sort(
307 items.begin(),
308 items.end(),
309 [&collator](Item* item1, Item* item2)
310 {
311 if(item1->toItem() == nullptr || item2->toItem() == nullptr)
312 return collator.compare("", "") < 0;
313
314 return collator.compare(item1->toItem()->getReadable(), item2->toItem()->getReadable()) < 0;
315 });
316
318 itemsChanged();
319}
320
321void ItemStorageBox::load(SaveFile* saveFile, int offset)
322{
323 reset();
324
325 this->file = saveFile;
326
327 if(saveFile == nullptr)
328 return;
329
330 auto toolset = saveFile->toolset;
331
332 auto it = saveFile->iterator()->offsetTo(offset+1);
333
334 for (var8 i = 0; i < toolset->getByte(offset) && i < maxSize; i++) {
335 auto item = new Item(it);
336 items.append(item);
337
338 connect(item, &Item::itemChanged, this, &ItemStorageBox::itemsChanged);
340 }
341
342 itemsChanged();
343
344 delete it;
345}
346
347void ItemStorageBox::save(SaveFile* saveFile, int offset)
348{
349 // Save all box items
350 auto it = saveFile->iterator()->offsetTo(offset);
351 it->setByte(items.size());
352 for (var8 i = 0; i < items.size() && i < maxSize; i++) {
353 it->setByte(items.at(i)->ind);
354 it->setByte(items.at(i)->amount);
355 }
356 it->setByte(0xFF);
357 delete it;
358}
359
361{
362 return (isBag)
363 ? file->dataExpanded->storage->items
364 : file->dataExpanded->player->items;
365}
366
368{
369 int ret = 0;
370
371 for(auto el : items) {
372 ret += el->buyPriceAllMoney();
373 }
374
375 return ret;
376}
377
379{
380 int ret = 0;
381
382 for(auto el : items) {
383 ret += el->buyPriceAllCoins();
384 }
385
386 return ret;
387}
388
390{
391 int ret = 0;
392
393 for(auto el : items) {
394 ret += el->sellPriceAllMoney();
395 }
396
397 return ret;
398}
399
401{
402 int ret = 0;
403
404 for(auto el : items) {
405 ret += el->sellPriceAllCoins();
406 }
407
408 return ret;
409}
410
412{
413 for(auto item : items) {
414 item->deleteLater();
415 }
416
417 items.clear();
419 itemsChanged();
420}
421
423{
424 reset();
425
426 if(isBag)
427 randomizeBag();
428 else
430
431 // Re-roll sorts by default (itemNew already guarantees no duplicates).
432 sort();
433
434 itemsChanged();
435}
void sort()
Sort the box contents.
QVector< Item * > items
The stored items.
void save(SaveFile *saveFile, int offset)
Flatten the box to the save.
void reset()
Empty the box.
bool getIsBag()
Is this the bag?
void itemsResetChange()
The box was reset.
void randomizeBag()
Randomizer path for the bag.
bool isBag
Bag vs PC (set at construction; treat as read-only).
void load(SaveFile *saveFile=nullptr, int offset=0)
Expand the box from the save.
ItemStorageBox * destBox()
The paired box for relocation.
virtual ~ItemStorageBox()
int maxSize
Capacity (set at construction; treat as read-only).
void itemNew()
Add a fresh random item (never a duplicate of one already here).
ItemStorageBox(bool isBag, int maxSize, SaveFile *saveFile=nullptr, int offset=0)
How many items are there.
int itemsCountBulk()
Item count including stack amounts.
void itemInsertChange()
An item was inserted.
void itemMoveChange(int from, int to)
An item moved slot.
bool relocateAll()
Move every item to the paired box.
int randomUniqueInd()
A random non-glitch/non-once item index absent from this box, or -1 if none remain.
bool itemMove(int from, int to)
Reorder an item.
bool relocateOne(int ind)
Move one item to the paired box.
void randomizeStorage()
Randomizer path for a PC item box.
int itemsMax()
Capacity.
bool relocateFull()
Is relocation blocked because the paired box is full?
Item * itemAt(int ind)
Item slot ind (GC-protected return).
bool hasItemInd(int ind)
Does this box already contain an item with index ind? (Q_INVOKABLE: the SelectItem dropdown greys out...
void randomize()
Randomize (dispatches to bag/storage path); sorts afterward.
SaveFile * file
Owning save.
int amountOfInd(int ind)
Total amount of item ind across all rows in this box (Q_INVOKABLE: the SelectItem dropdown shows the ...
int itemsCount()
Distinct item count.
void itemRemoveChange(int ind)
An item was removed.
void itemsChanged()
Box contents changed.
void itemRemove(int ind)
Remove item ind.
One inventory slot: an item index and an amount, with live pricing.
Definition item.h:36
void itemChanged()
static ItemsDB * inst()
< Number of items.
Definition itemsdb.cpp:37
bool chanceSuccess(const int percent) const
Did a percent chance succeed?
Definition random.cpp:73
int rangeInclusive(const int start, const int end) const
Random integer in the closed interval [start, end].
Definition random.cpp:42
static Random * inst()
< Convenience 50% coin flip (integer path), readable from QML.
Definition random.cpp:31
void setByte(var8 val, var16 padding=0)
Write a byte at the cursor; advances.
SaveFileIterator * offsetTo(var16 val)
Move the cursor to an absolute offset. Returns this for chaining.
One loaded save: the raw 32 KB bytes, their expanded object tree, and the tools that move between the...
Definition savefile.h:46
SaveFileToolset * toolset
Tools to operate directly on the raw sav file data.
Definition savefile.h:117
SaveFileIterator * iterator()
Returns a unique iterator that's setup to iterate over the raw sav file data.
Definition savefile.cpp:53
var8e var8
Everyday 8-bit alias. Exact (not "fastest") to dodge the pointer-width bug noted above.
Definition types.h:124
qmlCppOwned() – protect Q_INVOKABLE QObject returns from QML's GC.
static T * qmlCppOwned(T *obj)
Hand QML CppOwnership of a C++-owned QObject returned from a Q_INVOKABLE.