diff --git a/src/lazer/game/PartyModeMgr.cpp b/src/lazer/game/PartyModeMgr.cpp index a6a0ebaaa..27f11ca8f 100644 --- a/src/lazer/game/PartyModeMgr.cpp +++ b/src/lazer/game/PartyModeMgr.cpp @@ -29,6 +29,7 @@ #include "utl/DataPointMgr.h" #include "utl/JobMgr.h" #include "utl/Locale.h" +#include "utl/Std.h" #include "utl/Symbol.h" #include @@ -191,11 +192,9 @@ PartyModeMgr::PartyModeMgr() : mFrameSmoothers() { mPerSongDifficulty = false; mCustomParty = false; mUsingPerSongOptions = false; - mSetPartyOptionsJob = nullptr; - mGetPartyOptionsJob = nullptr; - mGetPartySongQueueJob = nullptr; - mAddSongToPartySongQueueJob = nullptr; - mDeleteSongFromPartySongQueueJob = nullptr; + for (int i = 0; i < 5; i++) { + mPartyJobs[i] = nullptr; + } unk314 = false; unk324 = 0; } @@ -816,9 +815,8 @@ void PartyModeMgr::SendPartyOptionsToRC() { if (!profile) { BroadcastSyncMsg("skipped_sync"); } else { - mSetPartyOptionsJob = - new SetPartyOptionsJob(this, profile->GetOnlineID()->ToString()); - TheRockCentral.ManageJob(mSetPartyOptionsJob); + mPartyJobs[0] = new SetPartyOptionsJob(this, profile->GetOnlineID()->ToString()); + TheRockCentral.ManageJob(mPartyJobs[0]); } } @@ -827,15 +825,14 @@ void PartyModeMgr::GetPartyOptionsFromRC() { if (!profile) { BroadcastSyncMsg("skipped_sync"); } else { - mGetPartyOptionsJob = - new GetPartyOptionsJob(this, profile->GetOnlineID()->ToString()); - TheRockCentral.ManageJob(mGetPartyOptionsJob); + mPartyJobs[1] = new GetPartyOptionsJob(this, profile->GetOnlineID()->ToString()); + TheRockCentral.ManageJob(mPartyJobs[1]); } } void PartyModeMgr::ReadPartyOptions() { - mGetPartyOptionsJob->GetOptions(); - mGetPartyOptionsJob = nullptr; + ((GetPartyOptionsJob *)mPartyJobs[1])->GetOptions(); + mPartyJobs[1] = nullptr; BroadcastSyncMsg("options_updated"); } @@ -844,9 +841,9 @@ void PartyModeMgr::GetPartySongQueueFromRC() { if (!profile) { BroadcastSyncMsg("skipped_sync"); } else { - mGetPartySongQueueJob = + mPartyJobs[2] = new GetPartySongQueueJob(this, profile->GetOnlineID()->ToString()); - TheRockCentral.ManageJob(mGetPartySongQueueJob); + TheRockCentral.ManageJob(mPartyJobs[2]); } } @@ -855,10 +852,10 @@ void PartyModeMgr::DeleteSongFromRCPartySongQueue(int songID) { if (!profile) { BroadcastSyncMsg("skipped_sync"); } else { - mDeleteSongFromPartySongQueueJob = new DeleteSongFromPartySongQueueJob( + mPartyJobs[4] = new DeleteSongFromPartySongQueueJob( this, profile->GetOnlineID()->ToString(), songID ); - TheRockCentral.ManageJob(mDeleteSongFromPartySongQueueJob); + TheRockCentral.ManageJob(mPartyJobs[4]); } } @@ -867,10 +864,10 @@ void PartyModeMgr::AddNextSongToRCPartySongQueue() { if (!profile) { BroadcastSyncMsg("skipped_sync"); } else { - mAddSongToPartySongQueueJob = new AddSongToPartySongQueueJob( + mPartyJobs[3] = new AddSongToPartySongQueueJob( this, profile->GetOnlineID()->ToString(), unk308.front().mSongID ); - TheRockCentral.ManageJob(mAddSongToPartySongQueueJob); + TheRockCentral.ManageJob(mPartyJobs[3]); } } @@ -1381,52 +1378,41 @@ DataNode PartyModeMgr::OnMsg(const RCJobCompleteMsg &msg) { if (!msg.Success()) { MILO_LOG("[PartyModeMgr::OnMsg] Party net API failed.\n"); for (int i = 0; i < 5; i++) { - if (mSetPartyOptionsJob == msg.Job()) { - mSetPartyOptionsJob->Cancel(false); - mSetPartyOptionsJob = nullptr; + if (mPartyJobs[i] == msg.Job()) { + mPartyJobs[i]->Cancel(false); + mPartyJobs[i] = nullptr; } } BroadcastSyncMsg("skipped_sync"); return 1; } b = false; - if (msg.Job() == mSetPartyOptionsJob) { + if (msg.Job() == mPartyJobs[0]) { BroadcastSyncMsg("options_sent"); - mSetPartyOptionsJob = nullptr; + mPartyJobs[0] = nullptr; b = true; - } else { - if (msg.Job() == mGetPartyOptionsJob) { - ReadPartyOptions(); + } else if (msg.Job() == mPartyJobs[1]) { + ReadPartyOptions(); + } else if (msg.Job() == mPartyJobs[2]) { + ReadPartySongQueue(); + } else if (msg.Job() == mPartyJobs[4]) { + mPartyJobs[4] = nullptr; + BroadcastSyncMsg("song_queue_updated"); + b = true; + } else if (msg.Job() == mPartyJobs[3]) { + mPartyJobs[3] = nullptr; + unk308.pop_front(); + if (!unk308.empty()) { + AddNextSongToRCPartySongQueue(); } else { - if (msg.Job() == mGetPartySongQueueJob) { - ReadPartySongQueue(); - } else { - if (msg.Job() == mDeleteSongFromPartySongQueueJob) { - mDeleteSongFromPartySongQueueJob = nullptr; - } else { - if (msg.Job() != mAddSongToPartySongQueueJob) { - goto leave; - } - mAddSongToPartySongQueueJob = nullptr; - unk308.pop_front(); - if (!unk308.empty()) { - AddNextSongToRCPartySongQueue(); - b = true; - } else { - mAddSongToPartySongQueueJob = nullptr; - unk314 = false; - } - } - BroadcastSyncMsg("song_queue_updated"); - b = true; - } + mPartyJobs[3] = nullptr; + unk314 = false; + BroadcastSyncMsg("song_queue_updated"); } + b = true; } -leave: if (b) { - DataNode party("party"); - DataNode updated("updated"); - ThePlatformMgr.SmartGlassSend(0, DataArrayPtr(updated, party)); + ThePlatformMgr.SmartGlassSend(0, DataArrayPtr("updated", "party")); } return 1; } diff --git a/src/lazer/game/PartyModeMgr.h b/src/lazer/game/PartyModeMgr.h index 3cd1a2576..cc26cee77 100644 --- a/src/lazer/game/PartyModeMgr.h +++ b/src/lazer/game/PartyModeMgr.h @@ -264,11 +264,12 @@ class PartyModeMgr : public Hmx::Object, public ContentMgr::Callback { float unk2e8; float unk2ec; float mSixStarBonus; // 0x2f0 - SetPartyOptionsJob *mSetPartyOptionsJob; // 0x2f4 - GetPartyOptionsJob *mGetPartyOptionsJob; // 0x2f8 - GetPartySongQueueJob *mGetPartySongQueueJob; // 0x2fc - AddSongToPartySongQueueJob *mAddSongToPartySongQueueJob; // 0x300 - DeleteSongFromPartySongQueueJob *mDeleteSongFromPartySongQueueJob; // 0x304 + RCJob *mPartyJobs[5]; // 0x2f4 + // SetPartyOptionsJob [0] - 0x2f4 + // GetPartyOptionsJob [1] - 0x2f8 + // GetPartySongQueueJob [2] - 0x2fc + // AddSongToPartySongQueueJob [3]- 0x300 + // DeleteSongFromPartySongQueueJob [4] - 0x304 std::list unk308; int mCurrSyncedSongID; // 0x310 bool unk314; diff --git a/src/lazer/meta_ham/CampaignPerformer.cpp b/src/lazer/meta_ham/CampaignPerformer.cpp index c0d92744c..c8053057a 100644 --- a/src/lazer/meta_ham/CampaignPerformer.cpp +++ b/src/lazer/meta_ham/CampaignPerformer.cpp @@ -548,7 +548,8 @@ int CampaignPerformer::GetNumSongCrazeMoves(Symbol s) { void CampaignPerformer::BookmarkCurrentProgress() { HamProfile *pProfile = TheProfileMgr.GetActiveProfile(true); MILO_ASSERT(pProfile, 0x4e9); - for (int i = 0; i <= mDifficulty; i++) { + Difficulty diff = mDifficulty; // i hate it here + for (int i = 0; i <= diff; i++) { pProfile->AccessCampaignProgress((Difficulty)i).BookmarkCurrentProgress(); } } diff --git a/src/lazer/meta_ham/HamPanel.cpp b/src/lazer/meta_ham/HamPanel.cpp index e8dd7f98f..ddaa1ccfc 100644 --- a/src/lazer/meta_ham/HamPanel.cpp +++ b/src/lazer/meta_ham/HamPanel.cpp @@ -41,7 +41,16 @@ void HamPanel::Poll() { UIPanel::Poll(); } UIComponent *HamPanel::FocusComponent() { auto pEventDialog = TheHamUI.EventDialogPanel(); MILO_ASSERT(pEventDialog, 60); - return UIPanel::FocusComponent(); + if (pEventDialog->GetState() == kUp) { + if (pEventDialog == this) { + return UIPanel::FocusComponent(); + } + return pEventDialog->FocusComponent(); + } + if (!TheHamUI.GetOverlayPanel() || TheHamUI.GetOverlayPanel() == this) { + return UIPanel::FocusComponent(); + } + return TheHamUI.GetOverlayPanel()->FocusComponent(); } BEGIN_HANDLERS(HamPanel) diff --git a/src/lazer/meta_ham/HamStorePanel.cpp b/src/lazer/meta_ham/HamStorePanel.cpp index a5d6aea28..e03810eb0 100644 --- a/src/lazer/meta_ham/HamStorePanel.cpp +++ b/src/lazer/meta_ham/HamStorePanel.cpp @@ -26,6 +26,7 @@ #include "os/User.h" #include "stl/_vector.h" #include "ui/UI.h" +#include "utl/JobMgr.h" #include "utl/Loader.h" #include "utl/MakeString.h" #include "utl/NetCacheMgr.h" @@ -268,8 +269,19 @@ void HamStorePanel::CreateCartUIs() { static Symbol description("description"); static Symbol art("art"); static Symbol store_filter_song_import_offers("store_filter_song_import_offers"); - unkac.insert(unkac.begin(), 1, new HamStoreFilter(store_filter_shopping_cart)); + auto it = unkac.begin(); + unkac.insert(it, new HamStoreFilter(store_filter_shopping_cart)); unkac.push_back(new HamStoreFilter(store_filter_song_import_offers)); + + DataArrayPtr ptr; + ptr->Insert(ptr->Size(), store_checkout); + ptr->Insert(ptr->Size(), DataArrayPtr(type, fake)); + ptr->Insert(ptr->Size(), DataArrayPtr(name, "")); + ptr->Insert(ptr->Size(), DataArrayPtr(artist, "")); + ptr->Insert(ptr->Size(), DataArrayPtr(album_name, "")); + ptr->Insert(ptr->Size(), DataArrayPtr(description, "")); + ptr->Insert(ptr->Size(), DataArrayPtr(art, "avatar_theboombox_nomip_xbox.dxt")); + unk38.push_back(MakeNewOffer(ptr)); } bool HamStorePanel::IsSpecialOfferOwned(Symbol offer) const { @@ -378,7 +390,7 @@ void HamStorePanel::Poll() { unk154 = true; } else { if (!unka0->HasFailed()) { - goto tag; + goto exit; } MILO_NOTIFY("Request for %s failed.", GetIndexFile()); ExitError((StoreError)3); @@ -393,7 +405,7 @@ void HamStorePanel::Poll() { unk70 = true; } } -tag: +exit: if (unk156 && unk128 != 0) { if (unkf8.SplitMs() >= unk128) { RelockCart(); diff --git a/src/lazer/meta_ham/HamStoreProvider.cpp b/src/lazer/meta_ham/HamStoreProvider.cpp index 66ea415c0..016861a37 100644 --- a/src/lazer/meta_ham/HamStoreProvider.cpp +++ b/src/lazer/meta_ham/HamStoreProvider.cpp @@ -3,6 +3,8 @@ #include "macros.h" #include "meta/StoreOffer.h" #include "meta_ham/AppLabel.h" +#include "meta_ham/HamStorePanel.h" +#include "meta_ham/HamUI.h" #include "obj/Data.h" #include "obj/Object.h" #include "os/Debug.h" @@ -13,6 +15,7 @@ #include "utl/NetCacheMgr.h" #include "utl/Std.h" #include "utl/Symbol.h" +#include "utl/trie.h" #pragma region PackSongListProvider @@ -57,7 +60,11 @@ HamStoreProvider::HamStoreProvider( } HamStoreProvider::~HamStoreProvider() { + FOREACH (it, unk38) { + RELEASE(it->second); + } unk38.clear(); + mFilteredOffers = nullptr; RELEASE(unk74); } @@ -171,12 +178,16 @@ Symbol HamStoreProvider::CurrentSort() const { } void HamStoreProvider::UpdateOffersInCart(StoreOffer *offer, int i) { - if (i == 0) { + switch (i) { + case 0: unkb0.push_back(offer); - } else if (i == 1) { + break; + case 1: unkb0.remove(offer); - } else if (i < 3) { + break; + case 2: unkb0.clear(); + break; } RefreshFilteredCartOffers(); } @@ -185,9 +196,7 @@ void HamStoreProvider::SetPackList(StoreOffer const *offer) { static Symbol pack("pack"); if (offer->OfferType() == pack) { static Symbol songs("songs"); - DataArrayPtr ptr = DataArrayPtr(songs); - unk78.mSongs = offer->GetData(ptr, false).Array(0); - unk78.mSongs->Release(); + unk78.mSongs = offer->GetData(DataArrayPtr(songs), false).Array(); } else { unk78.mSongs = 0; } @@ -222,20 +231,114 @@ void HamStoreProvider::ApplySort() { std::list *HamStoreProvider::GetOffersInCart() { return &unkb0; } +bool HamStoreProvider::ShowBrowserPurchased(const StoreOffer *offer) const { + static Symbol song("song"); + static Symbol pack("pack"); + if (offer->IsPurchased()) { + return true; + } else { + if (offer->OfferType() == song) { + const StoreOffer *currOffer = FindPack(offer); + if (currOffer && currOffer->HasSong(offer) && currOffer->IsPurchased()) { + return true; + } + } else if (offer->OfferType() == pack) { + for (int i = 0; i < offer->NumSongs(); i++) { + const StoreOffer *currOffer = FindSong(offer->Song(i)); + if (!currOffer || !currOffer->IsPurchased()) { + return false; + } + } + return offer->NumSongs() != 0; + } + } + return false; +} + +void HamStoreProvider::SetFilter(StoreOffer const *pack) { + MILO_ASSERT(pack->OfferType()=="pack", 0xb0); + unk50.clear(); + unk50.push_back((StoreOffer *)pack); + for (int i = 0; i < pack->NumSongs(); i++) { + const StoreOffer *offer = FindSong(pack->Song(i)); + if (offer && (offer->IsAvailable() || TheNetCacheMgr->IsDebug())) { + unk50.push_back((StoreOffer *)offer); + } + } + mFilteredOffers = &unk50; + mSorts.clear(); + mSortIndex = 0; +} + +void HamStoreProvider::PopulateOffersInCart() { + HamStorePanel *storePanel = dynamic_cast(TheHamUI.FocusPanel()); + MILO_ASSERT(storePanel, 0x206); + unkb0.clear(); + FOREACH_PTR (it_cartRow, unkac) { + CartRow &row = *it_cartRow; + FOREACH_PTR (it_storeOffer, unk30) { + StoreOffer *offer = *it_storeOffer; + if (offer->IsAvailable() || TheNetCacheMgr->IsDebug()) { + static Symbol song("song"); + if (offer->OfferType() == song && offer->GetSingleSongID() == row.unk0) { + if (offer->IsPurchased()) { + storePanel->RemoveDLCFromCart(offer->GetSingleSongID()); + } else { + unkb0.push_back(offer); + } + break; + } + } + } + } + RefreshFilteredCartOffers(); +} + +void HamStoreProvider::OnNextSort() { + MILO_ASSERT(AllowSortToggle(), 0xe8); + mSortIndex = (mSortIndex + 1) % mSorts.size(); + ApplySort(); +} + +void HamStoreProvider::SetFilter(HamStoreFilter const *filter) { + unk5c = (HamStoreFilter *)filter; + unk50.clear(); + std::map *>::iterator it; + if (unk5c && (it = unk38.find(unk5c->unk0), it != unk38.end())) { + mFilteredOffers = it->second; + mSorts = unk5c->unkc; + } else { + unk5c = nullptr; + mFilteredOffers = unk30; + mSorts.clear(); + } + mSortIndex = 0; + ApplySort(); +} + +bool HamStoreProvider::IsOfferInCart(StoreOffer *offer) { + FOREACH (it, unkb0) { + if (offer == *it) { + return true; + } + } + return false; +} + BEGIN_HANDLERS(HamStoreProvider) HANDLE_ACTION(refresh, Refresh()) HANDLE_EXPR(get_offer, OnGetOffer(_msg->Int(2))) HANDLE_ACTION(set_pack, SetPackList(_msg->Obj(2))) - HANDLE_EXPR(get_pack_provider, &GetPackProvider()) // recheck - HANDLE_ACTION(find_pack, FindPack(_msg->Obj(2))) + HANDLE_EXPR(get_pack_provider, &unk78) + HANDLE_EXPR(find_pack, (Hmx::Object *)FindPack(_msg->Obj(2))) HANDLE_EXPR(show_browser_purchased, ShowBrowserPurchased(_msg->Obj(2))) HANDLE_EXPR(show_unavailable, TheNetCacheMgr->IsDebug()) HANDLE_EXPR(is_partially_purchased, IsPartiallyPurchased(_msg->Obj(2))) - // HANDLE_EXPR(allow_sort_toggle, expr) + HANDLE_EXPR(allow_sort_toggle, mSorts.size() > 1) HANDLE_EXPR(get_current_sort_time, CurrentSort()) HANDLE_ACTION(next_sort, OnNextSort()) - // is offer in cart - HANDLE_ACTION(find_song, FindSong(_msg->Int(2))) + HANDLE_EXPR(is_offer_in_cart, IsOfferInCart(_msg->Obj(2))) + HANDLE_EXPR(find_song, (Hmx::Object *)FindSong(_msg->Int(2))) HANDLE_EXPR(get_offer_index, OnGetOfferIndex(_msg->Obj(2))) HANDLE_SUPERCLASS(UIListProvider) HANDLE_SUPERCLASS(Hmx::Object) diff --git a/src/lazer/meta_ham/HamStoreProvider.h b/src/lazer/meta_ham/HamStoreProvider.h index 76ff6daf0..5ef9c3954 100644 --- a/src/lazer/meta_ham/HamStoreProvider.h +++ b/src/lazer/meta_ham/HamStoreProvider.h @@ -49,6 +49,7 @@ class HamStoreProvider : public UIListProvider, public Hmx::Object { void OnNextSort(); void Refresh(); std::list *GetOffersInCart(); + bool IsOfferInCart(StoreOffer *); bool AllowSortToggle() { return mSorts.size() > 1; } PackSongListProvider GetPackProvider() { return unk78; } @@ -58,8 +59,8 @@ class HamStoreProvider : public UIListProvider, public Hmx::Object { std::vector *unk30; std::vector *unk34; std::map *> unk38; - std::vector unk54; - int unk5c; + std::vector unk50; + HamStoreFilter *unk5c; int mSortIndex; // 0x60 std::vector *mFilteredOffers; // 0x64 std::vector mSorts; // 0x68 diff --git a/src/lazer/meta_ham/Leaderboards.cpp b/src/lazer/meta_ham/Leaderboards.cpp index 56ac258fe..e82756b77 100644 --- a/src/lazer/meta_ham/Leaderboards.cpp +++ b/src/lazer/meta_ham/Leaderboards.cpp @@ -21,6 +21,7 @@ #include "ui/UI.h" #include "ui/UIListLabel.h" #include "ui/UIPanel.h" +#include "utl/Std.h" #include "utl/Symbol.h" Leaderboards::Leaderboards() : unk7c(100), unk80(0), unk84(0), unk88(2) { @@ -63,7 +64,10 @@ void Leaderboards::Text(int, int data, UIListLabel *slot, UILabel *label) const label->SetTextToken(gNullStr); } } else if (slot->Matches("rank")) { - if (!unk94) { + if (unk94) { + label->SetTextToken(gNullStr); + + } else { if (unk58[data].unk1d && unk88 != 2) { static char sBuffer[20]; Hx_snprintf(sBuffer, 20, "%d%% ", unk58[data].unk10); @@ -73,8 +77,6 @@ void Leaderboards::Text(int, int data, UIListLabel *slot, UILabel *label) const static Symbol rank_fmt("rank_fmt"); label->SetInt(unk58[data].unk10, false); } - } else { - label->SetTextToken(gNullStr); } } else if (slot->Matches("difficulty")) { static Symbol beginner_short("beginner_short"); @@ -241,7 +243,7 @@ void Leaderboards::AddPendingProfile(HamProfile *pProfile) { } void Leaderboards::StartUploadingNextProfile() { - for (auto it = mPendingProfiles.begin(); it != mPendingProfiles.end(); ++it) { + FOREACH (it, mPendingProfiles) { unk54 = mPendingProfiles.front(); mPendingProfiles.pop_front(); unk54->GetSongStatusMgr()->GetScoresToUpload(unk30); diff --git a/src/lazer/meta_ham/SkeletonIdentifier.cpp b/src/lazer/meta_ham/SkeletonIdentifier.cpp index 60a4cff7d..969562404 100644 --- a/src/lazer/meta_ham/SkeletonIdentifier.cpp +++ b/src/lazer/meta_ham/SkeletonIdentifier.cpp @@ -150,8 +150,8 @@ void SkeletonIdentifier::NotifyOfRecognition(int i) const { bool check = true; Skeleton *skel = TheGestureMgr->GetSkeletonByEnrollmentIndex(i); if (skel) { - if (!IsAssociatedWithProfile(i)) { - check = !skel->ProfileMatched() == 0; + if (!IsAssociatedWithProfile(i) && !(skel->ProfileMatched() == false)) { + check = false; } if (check) { int playerID = TheGameData->GetPlayerFromSkeleton(*skel); @@ -205,7 +205,11 @@ void SkeletonIdentifier::SetUpInitialProfiles() { int pad1 = -1; for (int i = 0; i < 4; i++) { if (ThePlatformMgr.IsPadNumSignedIn(i)) { - pad0 = i; + if (pad0 == -1) { + pad0 = i; + } else if (pad1 == -1) { + pad1 = i; + } } } if (pad0 != -1) { diff --git a/src/system/meta/StoreOffer.h b/src/system/meta/StoreOffer.h index a992f5e5e..fb1a0c92a 100644 --- a/src/system/meta/StoreOffer.h +++ b/src/system/meta/StoreOffer.h @@ -14,8 +14,8 @@ class StorePurchaseable : public Hmx::Object { bool Exists() const; static unsigned long long OfferStringToID(char const *); char const *CostStr() const; - bool IsAvailable() { return isAvailable; } - bool IsPurchased() { return isPurchased; } + bool IsAvailable() const { return isAvailable; } + bool IsPurchased() const { return isPurchased; } unsigned long long SongID() const { return songID; } bool isAvailable; // 0x2c diff --git a/src/system/utl/JobMgr.h b/src/system/utl/JobMgr.h index b0a3eed37..2aac2639b 100644 --- a/src/system/utl/JobMgr.h +++ b/src/system/utl/JobMgr.h @@ -57,6 +57,40 @@ class SingleItemEnumJob : public Job { XOVERLAPPED unk28; }; +// class MultipleItemsEnumJob : public Job { +// public: +// virtual ~MultipleItemsEnumJob(); +// virtual void Start(); +// virtual bool IsFinished(); +// virtual void Cancel(Hmx::Object *); +// virtual void OnCompletion(Hmx::Object *); + +// MultipleItemsEnumJob(Hmx::Object *, int, std::vector &); + +// protected: +// Hmx::Object *unk8; +// int unkc; +// std::vector unk10; +// std::vector unk1c; +// std::vector unk28; +// bool unk34; +// int unk38; +// int unk3c; +// u32 filler[7]; +// }; + +// class SpecialOfferEnumJob : public MultipleItemsEnumJob { +// public: +// SpecialOfferEnumJob(Hmx::Object *, int, std::vector &); +// virtual void Start(); +// virtual bool IsFinished(); +// virtual void Cancel(Hmx::Object *); +// virtual void OnCompletion(Hmx::Object *); + +// protected: +// Hmx::Object *unk5c; +// }; + #include "obj/Msg.h" DECLARE_MESSAGE(SingleItemEnumCompleteMsg, "single_item_enum_complete")