diff --git a/src/data/pack.cpp b/src/data/pack.cpp index 034393bc..e128534d 100644 --- a/src/data/pack.cpp +++ b/src/data/pack.cpp @@ -190,6 +190,10 @@ IMPLEMENT_REFLECTION(PackType) { if (filter) select = SELECT_NO_REPLACE; else if (!items.empty()) select = SELECT_ALL; } + if (indeterminate(summary)) { + if (filter) summary = true; + else if (!items.empty()) summary = false; + } if (indeterminate(selectable)) { if (filter) selectable = false; else if (!items.empty()) selectable = true; @@ -211,12 +215,19 @@ IMPLEMENT_REFLECTION(PackItem) { PackType::PackType() : enabled(true) , selectable(indeterminate) - , summary(true) + , summary(indeterminate) , select(SELECT_AUTO) {} PackItem::PackItem() : amount(1) + , probability(1) +{} + +PackItem::PackItem(const String& name, int amount) + : name(name) + , amount(amount) + , probability(1) {} @@ -380,7 +391,7 @@ void PackInstance::generate(vector* out) { } else if (pack_type.select == SELECT_NO_REPLACE) { card_copies += requested_copies; // NOTE: there is no way to pick items without replacement - if (out) { + if (out && !cards.empty()) { // to prevent us from being too predictable for small sets, periodically reshuffle RandomRange gen_range(parent.gen); int max_per_batch = ((int)cards.size() + 1) / 2; @@ -394,7 +405,7 @@ void PackInstance::generate(vector* out) { } else if (pack_type.select == SELECT_CYCLIC) { size_t total = cards.size() + pack_type.items.size(); - if (total <= 0) total = 1; // prevent div by 0 + if (total == 0) return; // prevent div by 0 size_t div = requested_copies / total; size_t rem = requested_copies % total; for (size_t i = 0 ; i < total ; ++i) { diff --git a/src/data/pack.hpp b/src/data/pack.hpp index c41f892b..5883cab0 100644 --- a/src/data/pack.hpp +++ b/src/data/pack.hpp @@ -137,7 +137,7 @@ class PackType : public IntrusivePtrBase { String name; ///< Name of this pack Scriptable enabled; ///< Is this pack enabled? tribool selectable; ///< Is this pack listed in the UI? - bool summary; ///< Should the total be listed for this type? + tribool summary; ///< Should the total be listed for this type? PackSelectType select; ///< What cards/items to select OptionalScript filter; ///< Filter to select this type of cards vector items; ///< Subpacks in this pack @@ -153,6 +153,7 @@ class PackType : public IntrusivePtrBase { class PackItem : public IntrusivePtrBase { public: PackItem(); + PackItem(const String& name, int amount); String name; ///< Name of the pack to select cards from Scriptable amount; ///< Number of cards of this type diff --git a/src/gui/set/random_pack_panel.cpp b/src/gui/set/random_pack_panel.cpp index 2a7b19a2..23b59c06 100644 --- a/src/gui/set/random_pack_panel.cpp +++ b/src/gui/set/random_pack_panel.cpp @@ -25,7 +25,7 @@ DECLARE_TYPEOF_COLLECTION(PackItemP); DECLARE_TYPEOF_COLLECTION(PackItemRefP); #endif DECLARE_TYPEOF_COLLECTION(CardP); -DECLARE_TYPEOF_COLLECTION(RandomPackPanel::PackItem_for_typeof); +DECLARE_TYPEOF_COLLECTION(PackAmountPicker); // ----------------------------------------------------------------------------- : RandomCardList @@ -89,7 +89,8 @@ void RandomCardList::getItems(vector& out) const { class PackTotalsPanel : public wxPanel { public: #if USE_NEW_PACK_SYSTEM - PackTotalsPanel(Window* parent, int id, PackGenerator& generator) : wxPanel(parent,id), generator(generator) {} + PackTotalsPanel(Window* parent, int id, PackGenerator& generator, bool show_all = false) + : wxPanel(parent,id), generator(generator), show_all(show_all) {} #else PackTotalsPanel(Window* parent, int id) : wxPanel(parent,id) {} #endif @@ -108,6 +109,7 @@ class PackTotalsPanel : public wxPanel { void drawItem(DC& dc, int& y, const String& name, double value); #if USE_NEW_PACK_SYSTEM PackGenerator& generator; + bool show_all; #else map amounts; #endif @@ -131,7 +133,7 @@ void PackTotalsPanel::draw(DC& dc) { #if USE_NEW_PACK_SYSTEM FOR_EACH(pack, game->pack_types) { PackInstance& i = generator.get(pack); - if (pack->summary && i.has_cards()) { + if (pack->summary && (show_all || i.has_cards())) { drawItem(dc, y, tr(*game, pack->name, capitalize), i.get_card_copies()); total += (int)i.get_card_copies(); } @@ -168,7 +170,9 @@ wxSize PackTotalsPanel::DoGetBestSize() const { if (game && generator.set) { FOR_EACH(pack, game->pack_types) { PackInstance& i = generator.get(pack); - if (pack->summary && i.has_cards()) lines++; + if (pack->summary && (show_all || i.has_cards())) { + lines++; + } } } #else @@ -206,6 +210,133 @@ BEGIN_EVENT_TABLE(PackTotalsPanel, wxPanel) EVT_PAINT(PackTotalsPanel::onPaint) END_EVENT_TABLE() +// ----------------------------------------------------------------------------- : PackAmountPicker + +PackAmountPicker::PackAmountPicker(wxWindow* parent, wxFlexGridSizer* sizer, const PackTypeP& pack) + : pack(pack) + , label(new wxStaticText(parent, wxID_ANY, capitalize_sentence(pack->name))) + , value(new wxSpinCtrl(parent, ID_PACK_AMOUNT, _("0"), wxDefaultPosition, wxSize(50,-1))) +{ + sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL); + sizer->Add(value, 0, wxEXPAND | wxALIGN_CENTER); +} + +void PackAmountPicker::destroy(wxFlexGridSizer* sizer) { + sizer->Detach(label); + sizer->Detach(value); + delete label; + delete value; +} + +// ----------------------------------------------------------------------------- : CustomPackDialog +#if USE_NEW_PACK_SYSTEM + +class CustomPackDialog : public wxDialog { + public: + CustomPackDialog(Window* parent, const SetP& set, const PackTypeP& edited_pack); + PackTypeP get() const { return edited_pack; } + private: + DECLARE_EVENT_TABLE(); + + SetP set; + PackTypeP edited_pack; + PackGenerator generator; + wxTextCtrl* name; + PackTotalsPanel* totals; + vector pickers; + + void updateTotals(); + void storePack(); + void onAmountChange(wxSpinEvent&); + void onOk(wxCommandEvent&); +}; + +CustomPackDialog::CustomPackDialog(Window* parent, const SetP& set, const PackTypeP& edited_pack) + : wxDialog(parent, wxID_ANY, _TITLE_("custom pack"), wxDefaultPosition, wxSize(500,500)) + , set(set), edited_pack(edited_pack) +{ + // init ui + totals = new PackTotalsPanel(this, wxID_ANY, generator, true); + name = new wxTextCtrl(this, wxID_ANY, edited_pack ? edited_pack->name : _("custom pack")); + // init sizer + wxSizer* s = new wxBoxSizer(wxVERTICAL); + wxSizer* s2 = new wxStaticBoxSizer(wxHORIZONTAL, this, _LABEL_("pack name")); + s2->Add(name, 1, wxEXPAND | wxALL, 4); + s->Add(s2, 0, wxEXPAND | wxALL, 8); + wxSizer* s3 = new wxBoxSizer(wxHORIZONTAL); + wxSizer* s4 = new wxStaticBoxSizer(wxHORIZONTAL, this, _LABEL_("pack selection")); + wxFlexGridSizer* packsSizer = new wxFlexGridSizer(0, 2, 4, 8); + packsSizer->AddGrowableCol(0); + s4->Add(packsSizer, 1, wxEXPAND | wxALL & ~wxTOP, 4); + s3->Add(s4, 1, wxEXPAND, 8); + wxSizer* s5 = new wxStaticBoxSizer(wxHORIZONTAL, this, _LABEL_("pack totals")); + s5->Add(totals, 1, wxEXPAND | wxALL, 4); + s3->Add(s5, 1, wxEXPAND | wxLEFT, 8); + s->Add(s3, 0, wxEXPAND | wxALL & ~wxTOP, 8); + s->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxALL & ~wxTOP, 8); + // add spin controls + FOR_EACH(pack, set->game->pack_types) { + if (pack->selectable) continue; // this pack is already selectable from the main UI + PackAmountPicker pick(this, packsSizer, pack); + pickers.push_back(pick); + // set value if it is nonzero + if (edited_pack) { + FOR_EACH(i, edited_pack->items) { + if (i->name == pack->name) { + pick.value->SetValue(i->amount); + } + } + } + } + s->SetSizeHints(this); + SetSizer(s); + // update totals + generator.reset(set,0); + totals->setGame(set->game); + updateTotals(); +} + +void CustomPackDialog::updateTotals() { + generator.gen.seed(0); + int total_packs = 0; + FOR_EACH(pick,pickers) { + int copies = pick.value->GetValue(); + total_packs += copies; + generator.get(pick.pack).request_copy(copies); + } + generator.update_card_counts(); + // update UI + totals->Refresh(false); + FindWindow(wxID_OK)->Enable(total_packs > 0); +} + +void CustomPackDialog::storePack() { + edited_pack = new_intrusive(); + edited_pack->selectable = true; + edited_pack->select = SELECT_ALL; + edited_pack->name = name->GetValue(); + FOR_EACH(pick,pickers) { + int copies = pick.value->GetValue(); + if (copies > 0) { + edited_pack->items.push_back(new_intrusive2(pick.pack->name, copies)); + } + } +} + +void CustomPackDialog::onOk(wxCommandEvent&) { + storePack(); + EndModal(wxID_OK); +} +void CustomPackDialog::onAmountChange(wxSpinEvent&) { + updateTotals(); +} + +BEGIN_EVENT_TABLE(CustomPackDialog, wxDialog) + EVT_BUTTON (wxID_OK, CustomPackDialog::onOk) + EVT_SPINCTRL (ID_PACK_AMOUNT, CustomPackDialog::onAmountChange) +END_EVENT_TABLE() + +#endif // ----------------------------------------------------------------------------- : RandomPackPanel RandomPackPanel::RandomPackPanel(Window* parent, int id) @@ -242,7 +373,7 @@ void RandomPackPanel::initControls() { s4->Add(packsSizer, 1, wxEXPAND | wxALL & ~wxTOP, 4); s3->Add(s4, 1, wxEXPAND, 8); wxSizer* s5 = new wxStaticBoxSizer(wxHORIZONTAL, this, _LABEL_("pack totals")); - s5->Add(totals, 1, wxEXPAND | wxALL, 4); + s5->Add(totals, 1, wxEXPAND | wxALL, 4); s3->Add(s5, 1, wxEXPAND | wxLEFT, 8); wxSizer* s6 = new wxBoxSizer(wxVERTICAL); wxSizer* s7 = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("seed")); @@ -255,6 +386,7 @@ void RandomPackPanel::initControls() { //s6->AddStretchSpacer(); //s6->Add(generate_button, 0, wxTOP | wxALIGN_RIGHT, 8); s6->Add(generate_button, 1, wxTOP | wxEXPAND, 8); + s6->Add(new wxButton(this, ID_CUSTOM_PACK, _BUTTON_("custom pack")), 1, wxTOP | wxEXPAND, 8); s3->Add(s6, 0, wxEXPAND | wxLEFT, 8); s2->Add(s3, 0, wxEXPAND | wxALL & ~wxTOP, 4); s2->Add(card_list, 1, wxEXPAND); @@ -279,26 +411,15 @@ void RandomPackPanel::onChangeSet() { totals ->setGame(set->game); // remove old pack controls - FOR_EACH(i, packs) { - packsSizer->Detach(i.label); - packsSizer->Detach(i.value); - delete i.label; - delete i.value; - } - packs.clear(); + FOR_EACH(pick, pickers) pick.destroy(packsSizer); + pickers.clear(); // add pack controls FOR_EACH(pack, set->game->pack_types) { #if USE_NEW_PACK_SYSTEM if (pack->selectable) { #endif - PackItem i; - i.pack = pack; - i.label = new wxStaticText(this, wxID_ANY, capitalize_sentence(pack->name)); - i.value = new wxSpinCtrl(this, ID_PACK_AMOUNT, _("0"), wxDefaultPosition, wxSize(50,-1)); - packsSizer->Add(i.label, 0, wxALIGN_CENTER_VERTICAL); - packsSizer->Add(i.value, 0, wxEXPAND | wxALIGN_CENTER); - packs.push_back(i); + pickers.push_back(PackAmountPicker(this,packsSizer,pack)); #if USE_NEW_PACK_SYSTEM } #endif @@ -312,12 +433,12 @@ void RandomPackPanel::onChangeSet() { seed_fixed ->SetValue(!gs.pack_seed_random); seed->Enable(!gs.pack_seed_random); setSeed(gs.pack_seed); - FOR_EACH(i, packs) { - i.value->SetValue(gs.pack_amounts[i.pack->name]); + FOR_EACH(pick, pickers) { + pick.value->SetValue(gs.pack_amounts[pick.pack->name]); } #if USE_NEW_PACK_SYSTEM - generator.reset(set,0); + generator.reset(set,last_seed=getSeed()); #endif updateTotals(); } @@ -326,8 +447,8 @@ void RandomPackPanel::storeSettings() { if (!isInitialized()) return; GameSettings& gs = settings.gameSettingsFor(*set->game); gs.pack_seed_random = seed_random->GetValue(); - FOR_EACH(i, packs) { - gs.pack_amounts[i.pack->name] = i.value->GetValue(); + FOR_EACH(pick, pickers) { + gs.pack_amounts[pick.pack->name] = pick.value->GetValue(); } } @@ -340,6 +461,11 @@ void RandomPackPanel::initUI(wxToolBar* tb, wxMenuBar* mb) { initControls(); onChangeSet(); } + // this is a good moment to update, because the set has likely changed + #if USE_NEW_PACK_SYSTEM + generator.reset(set,last_seed); + updateTotals(); + #endif } void RandomPackPanel::destroyUI(wxToolBar* tb, wxMenuBar* mb) {} @@ -361,6 +487,11 @@ void RandomPackPanel::onCommand(int id) { seed->Enable(seed_fixed->GetValue()); break; } + case ID_CUSTOM_PACK: { + CustomPackDialog dlg(this, set, PackTypeP()); + dlg.ShowModal(); + break; + } } } @@ -371,13 +502,13 @@ void RandomPackPanel::updateTotals() { totals->clear(); #endif int total_packs = 0; - FOR_EACH(i,packs) { - int copies = i.value->GetValue(); + FOR_EACH(pick,pickers) { + int copies = pick.value->GetValue(); total_packs += copies; #if USE_NEW_PACK_SYSTEM - generator.get(i.pack).request_copy(copies); + generator.get(pick.pack).request_copy(copies); #else - totals->addPack(*i.pack, copies); + totals->addPack(*pick.pack, copies); #endif } #if USE_NEW_PACK_SYSTEM @@ -415,21 +546,21 @@ void RandomPackPanel::setSeed(int seed) { void RandomPackPanel::generate() { #if USE_NEW_PACK_SYSTEM - generator.reset(set,getSeed()); + generator.reset(set,last_seed=getSeed()); #else boost::mt19937 gen((unsigned)getSeed()); PackItemCache pack_cache(*set); #endif // add packs to card list card_list->reset(); - FOR_EACH(item,packs) { - int copies = item.value->GetValue(); + FOR_EACH(pick,pickers) { + int copies = pick.value->GetValue(); for (int i = 0 ; i < copies ; ++i) { #if USE_NEW_PACK_SYSTEM - generator.get(item.pack).request_copy(); + generator.get(pick.pack).request_copy(); generator.generate(card_list->cards); #else - card_list->add(pack_cache, gen, *item.pack); + card_list->add(pack_cache, gen, *pick.pack); #endif } } diff --git a/src/gui/set/random_pack_panel.hpp b/src/gui/set/random_pack_panel.hpp index ba9fd0d1..27265d12 100644 --- a/src/gui/set/random_pack_panel.hpp +++ b/src/gui/set/random_pack_panel.hpp @@ -20,6 +20,19 @@ class PackTotalsPanel; struct CardSelectEvent; DECLARE_POINTER_TYPE(PackType); +// ----------------------------------------------------------------------------- : Utility + +// for lists of spin controls +struct PackAmountPicker { + PackTypeP pack; + wxStaticText* label; + wxSpinCtrl* value; + + PackAmountPicker() {} + PackAmountPicker(wxWindow* parent, wxFlexGridSizer* sizer, const PackTypeP& pack); + void destroy(wxFlexGridSizer* sizer); +}; + // ----------------------------------------------------------------------------- : RandomPackPanel /// A SetWindowPanel for creating random booster packs @@ -59,16 +72,11 @@ class RandomPackPanel : public SetWindowPanel { wxButton* generate_button; wxRadioButton* seed_random, *seed_fixed; PackTotalsPanel* totals; - - struct PackItem { - PackTypeP pack; - wxStaticText* label; - wxSpinCtrl* value; - }; - vector packs; + vector pickers; #if USE_NEW_PACK_SYSTEM PackGenerator generator; + int last_seed; #endif /// Actual intialization of the controls diff --git a/src/util/window_id.hpp b/src/util/window_id.hpp index 65595c06..fc867949 100644 --- a/src/util/window_id.hpp +++ b/src/util/window_id.hpp @@ -201,6 +201,7 @@ enum ChildMenuID { , ID_SEED_RANDOM , ID_SEED_FIXED , ID_GENERATE_PACK +, ID_CUSTOM_PACK // SymbolFont (Format menu) , ID_INSERT_SYMBOL_MENU_MIN = 9001