From 8de09754e0f827d2f230e2283fca3da8fd327fda Mon Sep 17 00:00:00 2001 From: rjkiv <76180273+rjkiv@users.noreply.github.com> Date: Thu, 10 Oct 2024 03:02:11 -0700 Subject: [PATCH] more band3 TUs (#374) * bandsongmgr work * stats work * stats work * performer handler * a bunch of performer work * begin work on songdb * songdb work * scoring work --- config/SZBE69/symbols.txt | 32 +-- config/SZBE69_B8/objects.json | 2 +- src/band3/game/Band.h | 16 ++ src/band3/game/CrowdRating.h | 11 +- src/band3/game/Game.h | 1 + src/band3/game/GameConfig.h | 4 + src/band3/game/HeldNote.cpp | 6 +- src/band3/game/MultiplayerAnalyzer.cpp | 10 +- src/band3/game/MultiplayerAnalyzer.h | 36 +-- src/band3/game/Performer.cpp | 307 ++++++++++++++++---- src/band3/game/Performer.h | 52 +++- src/band3/game/Scoring.cpp | 134 ++++++++- src/band3/game/Scoring.h | 63 +++- src/band3/game/SongDB.cpp | 90 ++++++ src/band3/game/SongDB.h | 66 ++++- src/band3/game/Stats.cpp | 351 ++++++++++++++++++++--- src/band3/game/Stats.h | 193 +++++++------ src/band3/meta_band/BandSongMetadata.cpp | 23 +- src/band3/meta_band/BandSongMetadata.h | 2 + src/band3/meta_band/BandSongMgr.cpp | 98 ++++++- src/band3/meta_band/BandSongMgr.h | 25 +- src/band3/meta_band/PerformanceData.cpp | 2 +- src/system/beatmatch/SongData.h | 5 +- src/system/beatmatch/SongPos.h | 2 +- src/system/os/ContentMgr.h | 2 +- 25 files changed, 1236 insertions(+), 297 deletions(-) create mode 100644 src/band3/game/Band.h create mode 100644 src/band3/game/SongDB.cpp diff --git a/config/SZBE69/symbols.txt b/config/SZBE69/symbols.txt index 72ff9ee6..0dd28f2c 100644 --- a/config/SZBE69/symbols.txt +++ b/config/SZBE69/symbols.txt @@ -9612,7 +9612,7 @@ __ct__14RestartGameMsgFv = .text:0x8010DD74; // type:function size:0x44 NewNetMessage__20ResumeNoScoreGameMsgFv = .text:0x8010DDB8; // type:function size:0x30 NewNetMessage__14PlayerStatsMsgFv = .text:0x8010DDE8; // type:function size:0x30 __ct__14PlayerStatsMsgFv = .text:0x8010DE18; // type:function size:0x4C -fn_8010DE64 = .text:0x8010DE64; // type:function size:0xEC +__dt__5StatsFv = .text:0x8010DE64; // type:function size:0xEC fn_8010DF50 = .text:0x8010DF50; // type:function size:0x58 fn_8010DFA8 = .text:0x8010DFA8; // type:function size:0x80 fn_8010E028 = .text:0x8010E028; // type:function size:0x80 @@ -11313,7 +11313,7 @@ fn_80141D74 = .text:0x80141D74; // type:function size:0x108 fn_80141E7C = .text:0x80141E7C; // type:function size:0x5C fn_80141ED8 = .text:0x80141ED8; // type:function size:0x10 __ct__9PerformerFP8BandUserP4Band = .text:0x80141EE8; // type:function size:0x13C -fn_80142024 = .text:0x80142024; // type:function size:0x374 +__ct__5StatsFRC5Stats = .text:0x80142024; // type:function size:0x374 fn_80142398 = .text:0x80142398; // type:function size:0x30 fn_801423C8 = .text:0x801423C8; // type:function size:0x98 fn_80142460 = .text:0x80142460; // type:function size:0x70 @@ -12377,7 +12377,7 @@ fn_8015F13C = .text:0x8015F13C; // type:function size:0x8 fn_8015F144 = .text:0x8015F144; // type:function size:0x2C fn_8015F170 = .text:0x8015F170; // type:function size:0x2C fn_8015F19C = .text:0x8015F19C; // type:function size:0xC -fn_8015F1A8 = .text:0x8015F1A8; // type:function size:0x350 +__ct__5StatsFv = .text:0x8015F1A8; // type:function size:0x350 fn_8015F4F8 = .text:0x8015F4F8; // type:function size:0x30 fn_8015F528 = .text:0x8015F528; // type:function size:0x30 fn_8015F558 = .text:0x8015F558; // type:function size:0x40 @@ -12396,27 +12396,27 @@ fn_8015F910 = .text:0x8015F910; // type:function size:0x5C fn_8015F96C = .text:0x8015F96C; // type:function size:0x30 fn_8015F99C = .text:0x8015F99C; // type:function size:0x30 fn_8015F9CC = .text:0x8015F9CC; // type:function size:0x40 -fn_8015FA0C = .text:0x8015FA0C; // type:function size:0x64 +BuildHitStreak__5StatsFif = .text:0x8015FA0C; // type:function size:0x64 CurrentStreak__5StatsFv = .text:0x8015FA70; // type:function size:0x8 -fn_8015FA78 = .text:0x8015FA78; // type:function size:0x8 +SetCurrentStreak__5StatsFi = .text:0x8015FA78; // type:function size:0x8 LongestStreak__5StatsFv = .text:0x8015FA80; // type:function size:0x2C -fn_8015FAAC = .text:0x8015FAAC; // type:function size:0x38 -fn_8015FAE4 = .text:0x8015FAE4; // type:function size:0x3C -fn_8015FB20 = .text:0x8015FB20; // type:function size:0x18 -fn_8015FB38 = .text:0x8015FB38; // type:function size:0xC -fn_8015FB44 = .text:0x8015FB44; // type:function size:0x20 -fn_8015FB64 = .text:0x8015FB64; // type:function size:0x54 +SetPersistentStreak__5StatsFi = .text:0x8015FAAC; // type:function size:0x38 +EndHitStreak__5StatsFv = .text:0x8015FAE4; // type:function size:0x3C +BuildMissStreak__5StatsFi = .text:0x8015FB20; // type:function size:0x18 +EndMissStreak__5StatsFv = .text:0x8015FB38; // type:function size:0xC +BuildStreak__5StatsFRQ25Stats10StreakInfoi = .text:0x8015FB44; // type:function size:0x20 +EndStreak__5StatsFRQ25Stats10StreakInfoRQ211stlpmtx_std83vector> = .text:0x8015FB64; // type:function size:0x54 fn_8015FBB8 = .text:0x8015FBB8; // type:function size:0x80 fn_8015FC38 = .text:0x8015FC38; // type:function size:0x4 fn_8015FC3C = .text:0x8015FC3C; // type:function size:0x3C fn_8015FC78 = .text:0x8015FC78; // type:function size:0x2C -fn_8015FCA4 = .text:0x8015FCA4; // type:function size:0x68 -fn_8015FD0C = .text:0x8015FD0C; // type:function size:0x50 +SetFinalized__5StatsFb = .text:0x8015FCA4; // type:function size:0x68 +UpdateBestSolo__5StatsFi = .text:0x8015FD0C; // type:function size:0x50 fn_8015FD5C = .text:0x8015FD5C; // type:function size:0x7C fn_8015FDD8 = .text:0x8015FDD8; // type:function size:0x4 fn_8015FDDC = .text:0x8015FDDC; // type:function size:0x3C fn_8015FE18 = .text:0x8015FE18; // type:function size:0x2C -fn_8015FE44 = .text:0x8015FE44; // type:function size:0x94 +SetVocalSingerAndPartCounts__5StatsFii = .text:0x8015FE44; // type:function size:0x94 fn_8015FED8 = .text:0x8015FED8; // type:function size:0x4 fn_8015FEDC = .text:0x8015FEDC; // type:function size:0x88 fn_8015FF64 = .text:0x8015FF64; // type:function size:0x2C @@ -12542,11 +12542,11 @@ fn_8016261C = .text:0x8016261C; // type:function size:0x18 fn_80162634 = .text:0x80162634; // type:function size:0x4 fn_80162638 = .text:0x80162638; // type:function size:0xC fn_80162644 = .text:0x80162644; // type:function size:0x14 -fn_80162658 = .text:0x80162658; // type:function size:0x14 +__ct__Q25Stats10StreakInfoFv = .text:0x80162658; // type:function size:0x14 fn_8016266C = .text:0x8016266C; // type:function size:0x20 fn_8016268C = .text:0x8016268C; // type:function size:0x4C fn_801626D8 = .text:0x801626D8; // type:function size:0x48 -fn_80162720 = .text:0x80162720; // type:function size:0x2C +__ct__Q25Stats14MultiplierInfoFv = .text:0x80162720; // type:function size:0x2C fn_8016274C = .text:0x8016274C; // type:function size:0x38 fn_80162784 = .text:0x80162784; // type:function size:0x48 fn_801627CC = .text:0x801627CC; // type:function size:0x54 diff --git a/config/SZBE69_B8/objects.json b/config/SZBE69_B8/objects.json index d9e18038..fddf538e 100644 --- a/config/SZBE69_B8/objects.json +++ b/config/SZBE69_B8/objects.json @@ -81,7 +81,7 @@ "band3/game/Scoring.cpp": "MISSING", "band3/game/Shuttle.cpp": "NonMatching", "band3/game/Singer.cpp": "MISSING", - "band3/game/SongDB.cpp": "MISSING", + "band3/game/SongDB.cpp": "NonMatching", "band3/game/StatCollector.cpp": "MISSING", "band3/game/StatMemberTracker.cpp": "MISSING", "band3/game/Stats.cpp": "NonMatching", diff --git a/src/band3/game/Band.h b/src/band3/game/Band.h new file mode 100644 index 00000000..3f1a3a7c --- /dev/null +++ b/src/band3/game/Band.h @@ -0,0 +1,16 @@ +#pragma once +#include "obj/Object.h" + +class BandUser; +class BeatMaster; + +class Band : public Hmx::Object { +public: + Band(bool, int, BandUser*, BeatMaster*); + virtual ~Band(); + virtual DataNode Handle(DataArray*, bool); + + int EnergyMultiplier() const; + int EnergyCrowdBoost() const; + void ForceStars(int); +}; \ No newline at end of file diff --git a/src/band3/game/CrowdRating.h b/src/band3/game/CrowdRating.h index 55073224..499af082 100644 --- a/src/band3/game/CrowdRating.h +++ b/src/band3/game/CrowdRating.h @@ -11,8 +11,17 @@ class CrowdRating { void Reset(); void Configure(BandUser*, Difficulty); + float GetThreshold(ExcitementLevel) const; + float GetMinValue(){ return unk10; } + bool IsActive() const { return mActive; } + float GetValue() const { return unkc; } + void SetActive(bool); + void SetValue(float); + bool IsInWarning() const; + float GetRawValue() const { return unk8; } + void SetDisplayValue(float); - bool unk4; + bool mActive; // 0x4 float unk8; float unkc; float unk10; diff --git a/src/band3/game/Game.h b/src/band3/game/Game.h index 2626a574..1185a80b 100644 --- a/src/band3/game/Game.h +++ b/src/band3/game/Game.h @@ -10,6 +10,7 @@ class Game : public Hmx::Object { virtual ~Game(); void SetPaused(bool, bool, bool); + void SetGameOver(bool); u8 pad[0x4C]; bool mIsPaused; // 0x68 iunno diff --git a/src/band3/game/GameConfig.h b/src/band3/game/GameConfig.h index 38e860eb..78a509b2 100644 --- a/src/band3/game/GameConfig.h +++ b/src/band3/game/GameConfig.h @@ -2,6 +2,7 @@ #include "obj/Object.h" #include "beatmatch/PlayerTrackConfig.h" #include "game/PracticeSectionProvider.h" +#include "game/Defines.h" class GameConfig : public Hmx::Object { public: @@ -10,6 +11,9 @@ class GameConfig : public Hmx::Object { virtual DataNode Handle(DataArray*, bool); virtual bool SyncProperty(DataNode&, DataArray*, int, PropOp); + Difficulty GetAverageDifficulty() const; + bool CanEndGame() const; + PlayerTrackConfigList* mPlayerTrackConfigList; // 0x1c PracticeSectionProvider* mPracticeSectionProvider; // 0x20 float mSongLimitMs; // 0x24 diff --git a/src/band3/game/HeldNote.cpp b/src/band3/game/HeldNote.cpp index d86fd94b..938143e5 100644 --- a/src/band3/game/HeldNote.cpp +++ b/src/band3/game/HeldNote.cpp @@ -18,7 +18,7 @@ HeldNote::HeldNote( unk_0x20 = true; - int tailPoints = TheScoring.GetTailPoints(trackType, ticks); + int tailPoints = TheScoring->GetTailPoints(trackType, ticks); unk_0x10 = tailPoints * bits; unk_0x18 = tailPoints * slots; @@ -98,7 +98,7 @@ double HeldNote::SetHoldTime(float time) { } float HeldNote::GetPointFraction() { - int headPoints = TheScoring.GetHeadPoints(mTrackType); + int headPoints = TheScoring->GetHeadPoints(mTrackType); int pointsPlus = headPoints + unk_0x18; if (pointsPlus) { @@ -143,7 +143,7 @@ void HeldNote::ReleaseSlot(int slot) { unk_0x1c = (unsigned int)gem; int bits = gem->CountBitsInSlotType(mask); - int tailPoints = TheScoring.GetTailPoints(mTrackType, mGameGem->mDurationTicks); + int tailPoints = TheScoring->GetTailPoints(mTrackType, mGameGem->mDurationTicks); unk_0x10 = tailPoints * bits; unk_0x20 = false; unk_0xc *= bits / (bits + 1); diff --git a/src/band3/game/MultiplayerAnalyzer.cpp b/src/band3/game/MultiplayerAnalyzer.cpp index f716f5e4..27d8ea65 100644 --- a/src/band3/game/MultiplayerAnalyzer.cpp +++ b/src/band3/game/MultiplayerAnalyzer.cpp @@ -4,7 +4,7 @@ MultiplayerAnalyzer::MultiplayerAnalyzer(SongData *songData) : mUnk_0x0(""), mSongData(songData), mUnk_0x10(), mUnk_0x18() {} float MultiplayerAnalyzer::GetMaxPoints(const UserGuid &userGuid) const { - const MysteryType *data = GetData(userGuid); + const Data *data = GetData(userGuid); if (data == 0) { return 0; @@ -14,7 +14,7 @@ float MultiplayerAnalyzer::GetMaxPoints(const UserGuid &userGuid) const { } float MultiplayerAnalyzer::GetMaxStreakPoints(const UserGuid &userGuid) const { - const MysteryType *data = GetData(userGuid); + const Data *data = GetData(userGuid); if (data == 0) { return 0; @@ -24,7 +24,7 @@ float MultiplayerAnalyzer::GetMaxStreakPoints(const UserGuid &userGuid) const { } float MultiplayerAnalyzer::GetBonusPoints(const UserGuid &userGuid) const { - const MysteryType *data = GetData(userGuid); + const Data *data = GetData(userGuid); if (data == 0) { return 0.0f; @@ -33,7 +33,7 @@ float MultiplayerAnalyzer::GetBonusPoints(const UserGuid &userGuid) const { return data->unk_0x20 + data->unk_0x24; } -const MysteryType *MultiplayerAnalyzer::GetData(const UserGuid &userGuid) const { +const MultiplayerAnalyzer::Data *MultiplayerAnalyzer::GetData(const UserGuid &userGuid) const { for (int i = 0; i < mUnk_0x8.size(); i++) { if (mUnk_0x8[i].mGuid == userGuid) { return &mUnk_0x8[i]; @@ -43,7 +43,7 @@ const MysteryType *MultiplayerAnalyzer::GetData(const UserGuid &userGuid) const return 0; } -MysteryType *MultiplayerAnalyzer::GetData(const UserGuid &userGuid) { +MultiplayerAnalyzer::Data *MultiplayerAnalyzer::GetData(const UserGuid &userGuid) { for (int i = 0; i < mUnk_0x8.size(); i++) { if (mUnk_0x8[i].mGuid == userGuid) { return &mUnk_0x8[i]; diff --git a/src/band3/game/MultiplayerAnalyzer.h b/src/band3/game/MultiplayerAnalyzer.h index 4882a606..32f06ef2 100644 --- a/src/band3/game/MultiplayerAnalyzer.h +++ b/src/band3/game/MultiplayerAnalyzer.h @@ -1,24 +1,27 @@ #ifndef GAME_MULTIPLAYERANALYZER_H #define GAME_MULTIPLAYERANALYZER_H - #include "beatmatch/SongData.h" #include "system/utl/HxGuid.h" -class MysteryType { -public: - HxGuid mGuid; - char unk_pre[8]; - float m_maxStreakPoints; - float m_maxPoints; - float unk_0x20; - float unk_0x24; - char unk_Stuff[24]; -}; - class PlayerScoreInfo {}; class MultiplayerAnalyzer { public: + class Data { + public: + HxGuid mGuid; + char unk_pre[8]; + float m_maxStreakPoints; + float m_maxPoints; + float unk_0x20; + float unk_0x24; + char unk_Stuff[24]; + }; + + class GemScore { + + }; + MultiplayerAnalyzer(SongData *); void PostLoad(); @@ -35,16 +38,17 @@ class MultiplayerAnalyzer { void GetCodaExtents(const UserGuid &, int &, int &); void AddGems(); void AddCodas(); + void OverrideBasePoints(int, TrackType, const UserGuid&, int, int, int); - const MysteryType *GetData(const UserGuid &) const; - MysteryType *GetData(const UserGuid &); + const Data *GetData(const UserGuid &) const; + Data *GetData(const UserGuid &); char *mUnk_0x0; SongData *mSongData; // Whatever type is here is 0x40 size - std::vector mUnk_0x8; - std::vector mUnk_0x10; + std::vector mUnk_0x8; + std::vector mUnk_0x10; // 0x10 - base scores int mUnk_0x18; }; diff --git a/src/band3/game/Performer.cpp b/src/band3/game/Performer.cpp index e0a2b5ec..60e25620 100644 --- a/src/band3/game/Performer.cpp +++ b/src/band3/game/Performer.cpp @@ -1,63 +1,250 @@ #include "Stats.h" #include "game/Performer.h" +#include "game/GameConfig.h" +#include "game/BandUser.h" +#include "game/Band.h" +#include "game/Game.h" +#include "utl/Symbols.h" +#include "utl/Messages.h" -Performer::Performer(BandUser* user, Band* band) : unk8(0), unk10(Stats()), unk1dc(band), unk1e0(0), unk1e1(0), unk1e2(0), unk1e4(0), unk1fc(0), unk1fd(1), unk1fe(1), unk1ff(1), unk200(0), - unk204(0), unk205(1), unk208(0) { - unkc = new CrowdRating(user, kDifficultyEasy); -} - -SongPos::SongPos() : mTotalTick(0), mMeasure(0), mBeat(0), mTick(0) {} - -Stats::Stats(const Stats& copy) { - mHitCount = copy.mHitCount; - mMissCount = copy.mMissCount; - m0x08 = copy.m0x08; - m0x0c = copy.m0x0c; - m0x10 = copy.m0x10; - m0x14 = copy.m0x14; - mNotesHitFraction = copy.mNotesHitFraction; - mFailedDeploy = copy.mFailedDeploy; - mDeployCount = copy.mDeployCount; - mFillHitCount = copy.mFillHitCount; - m0x28 = copy.m0x28; - m0x2c = copy.m0x2c; - m0x30 = copy.m0x30; - m0x34 = copy.m0x34; - mFinalized = copy.mFinalized; - mSoloPercentage = copy.mSoloPercentage; - m0x3c = copy.m0x3c; - mPerfectSoloWithSoloButtons = copy.mPerfectSoloWithSoloButtons; - m0x41 = copy.m0x41; - mNumberOfSingers = copy.mNumberOfSingers; - m0x48 = copy.m0x48; - mDoubleHarmonyHit = copy.mDoubleHarmonyHit; - mDoubleHarmonyPhraseCount = copy.mDoubleHarmonyPhraseCount; - mTripleHarmonyHit = copy.mTripleHarmonyHit; - mTripleHarmonyPhraseCount = copy.mTripleHarmonyPhraseCount; - m0x5c = copy.m0x5c; - m0x60 = copy.m0x60; - m0x64 = copy.m0x64; - m0x68 = copy.m0x68; - m0x6c = copy.m0x6c; - m0x70 = copy.m0x70; - m0x78 = copy.m0x78; - -} - -int Stats::GetDoubleHarmonyHit() const { return mDoubleHarmonyHit; } -int Stats::GetDoubleHarmonyPhraseCount() const { return mDoubleHarmonyPhraseCount; } -int Stats::GetTripleHarmonyHit() const { return mTripleHarmonyHit; } -int Stats::GetTripleHarmonyPhraseCount() const { return mTripleHarmonyPhraseCount; } -int Stats::GetHitCount() const { return mHitCount; } -float Stats::GetNotesHitFraction() const { return mNotesHitFraction; } -int Stats::GetNumberOfSingers() const { return mNumberOfSingers; } -void Stats::GetVocalPartPercentage(int) const {} -bool Stats::GetFailedDeploy() const { return mFailedDeploy; } -int Stats::GetPlayersSaved() const { return mPlayersSaved; } -int Stats::GetFillHitCount() const { return mFillHitCount; } -void Stats::GetStrummedDown() const {} -void Stats::GetStrummedUp() const {} -int Stats::GetDeployCount() const { return mDeployCount; } -int Stats::GetSoloPercentage() const { return mSoloPercentage; } -bool Stats::GetPerfectSoloWithSoloButtons() const { return mPerfectSoloWithSoloButtons; } -bool Stats::GetFinalized() const { return mFinalized; } \ No newline at end of file +#pragma push +#pragma dont_inline on +Performer::Performer(BandUser* user, Band* band) : mPollMs(0), mStats(Stats()), mBand(band), unk1e0(0), unk1e1(0), unk1e2(0), mScore(0), unk1fc(0), unk1fd(1), unk1fe(1), unk1ff(1), mProgressMs(0), + unk204(0), mMultiplierActive(1), mNumRestarts(0) { + Difficulty diff = !user ? TheGameConfig->GetAverageDifficulty() : user->GetDifficulty(); + mCrowd = new CrowdRating(user, diff); +} +#pragma pop + +Performer::~Performer(){ + RELEASE(mCrowd); +} + +int Performer::GetScore() const { + if(mStats.FailedNoScore()) return 0; + else return mScore + 0.01; +} + +int Performer::GetIndividualScore() const { + int score = GetScore(); + if(score > 0) return score - (int)mStats.GetBandContribution(); + else return 0; +} + +int Performer::GetMultiplier(bool b, int& i1, int& i2, int& i3) const { + i1 = 1; + i2 = 1; + i3 = 1; + if(mMultiplierActive){ + i2 = mBand->EnergyMultiplier(); + return i2; + } + else return 1; +} + +float Performer::GetCrowdRating() const { + return mCrowd->GetValue(); +} + +float Performer::GetCrowdWarningLevel() const { + return mCrowd->GetValue(); +} + +float Performer::GetRawCrowdRating() const { + return mCrowd->GetRawValue(); +} + +bool Performer::IsInCrowdWarning() const { + return mCrowd->IsInWarning(); +} + +float Performer::PollMs() const { return mPollMs; } + +float Performer::GetCrowdBoost() const { + return mBand->EnergyCrowdBoost(); +} + +#pragma push +#pragma dont_inline on +void Performer::Restart(bool b){ + mPollMs = 0; + mProgressMs = 0; + mScore = 0; + unk204 = false; + if(!b) mStats = Stats(); + unk1e0 = 0; + unk1e1 = 0; + unk1e2 = 0; + mCrowd->Reset(); + mNumRestarts++; +} +#pragma pop + +ExcitementLevel Performer::GetExcitement() const { + return mCrowd->GetExcitement(); +} + +void Performer::SetMultiplierActive(bool b){ mMultiplierActive = b; } +bool Performer::GetMultiplierActive() const { return mMultiplierActive; } +void Performer::SetCrowdMeterActive(bool b){ mCrowd->SetActive(b); } +bool Performer::GetCrowdMeterActive(){ return mCrowd->IsActive(); } + +void Performer::UpdateScore(int i){ + if(IsNet()) mScore = i; +} + +void Performer::ForceScore(int i){ mScore = i; } + +void Performer::SetStats(int i, const Stats& stats){ + mStats = stats; + mStats.SetFinalized(true); + mScore = i; +} + +void Performer::BuildHitStreak(int i, float f){ + if(IsLocal()){ + mStats.BuildHitStreak(i, f); + SendStreak(); + } +} + +void Performer::EndHitStreak(){ + if(IsLocal()){ + mStats.EndHitStreak(); + SendStreak(); + } +} + +void Performer::BuildMissStreak(int i){ + if(IsLocal()){ + mStats.BuildMissStreak(i); + } +} + +void Performer::EndMissStreak(){ + if(IsLocal()){ + mStats.EndMissStreak(); + } +} + +void Performer::SendStreak(){ + MILO_ASSERT(IsLocal(), 0x170); + if(unk1fe){ + Handle(send_streak_msg, false); + } +} + +void Performer::SetRemoteStreak(int i){ + if(IsNet()){ + mStats.SetCurrentStreak(i); + } +} + +void Performer::TrulyWinGame(){ + if(unk204 || !TheGameConfig->CanEndGame()) return; + else { + TheGame->SetGameOver(true); + unk204 = true; + } +} + +void Performer::WinGame(int i){ + if(i > 0){ + mBand->ForceStars(i); + TrulyWinGame(); + } + if(IsLocal()){ + unk1e2 = true; + Handle(send_finished_song_msg, false); + } +} + +void Performer::ForceStars(int i){ + mScore = GetScoreForStars(i); +} + +bool Performer::LoseGame(){ + if(unk204 || !TheGameConfig->CanEndGame() || !TheGame->mIsPaused) return false; // TODO: fix the variable pulled from TheGame + else { + mCrowd->SetActive(false); + TheGame->SetGameOver(false); + SetLost(); + return true; + } +} + +void Performer::SetLost(){ + unk1e0 = true; + unk204 = true; +} + +void Performer::RemoteUpdateCrowd(float f){ + mCrowd->SetDisplayValue(f); +} + +void Performer::RemoteFinishedSong(int i){ + UpdateScore(i); + unk1e2 = true; +} + +int Performer::GetNumRestarts() const { return mNumRestarts; } + +void Performer::SetNoScorePercent(float f){ + mScore = 0; + mStats.SetNoScorePercent(f); +} + +#pragma push +#pragma dont_inline on +BEGIN_HANDLERS(Performer) + HANDLE_EXPR(percent_complete, GetPercentComplete()) + HANDLE_EXPR(progress_ms, mProgressMs) + HANDLE_ACTION(finalize_stats, FinalizeStats()) + HANDLE_EXPR(notes_hit, mStats.GetHitCount()) + HANDLE_EXPR(current_notes_hit_fraction, GetNotesHitFraction(0)) + HANDLE_EXPR(notes_hit_fraction, mStats.GetNotesHitFraction()) + HANDLE_EXPR(current_streak, mStats.GetCurrentStreak()) + HANDLE_EXPR(longest_streak, mStats.GetLongestStreak()) + HANDLE_EXPR(get_singer_count, mStats.GetNumberOfSingers()) + HANDLE_EXPR(get_singer_ranked_percentage, mStats.GetSingerRankedPercentage(_msg->Int(2), _msg->Int(3))) + HANDLE_EXPR(get_singer_ranked_part, mStats.GetSingerRankedPart(_msg->Int(2), _msg->Int(3))) + HANDLE_EXPR(get_vocal_part_percentage, mStats.GetVocalPartPercentage(_msg->Int(2))) + HANDLE_EXPR(get_double_harmony_hit, mStats.GetDoubleHarmonyHit()) + HANDLE_EXPR(get_double_harmony_total, mStats.GetDoubleHarmonyPhraseCount()) + HANDLE_EXPR(get_triple_harmony_hit, mStats.GetTripleHarmonyHit()) + HANDLE_EXPR(get_triple_harmony_total, mStats.GetTripleHarmonyPhraseCount()) + HANDLE_EXPR(get_song_num_vocal_parts, GetSongNumVocalParts()) + HANDLE_EXPR(failed_deploy, mStats.GetFailedDeploy()) + HANDLE_EXPR(saved_count, mStats.GetPlayersSaved()) + HANDLE_EXPR(fill_hit_count, mStats.GetFillHitCount()) + HANDLE_EXPR(strummed_down, mStats.GetStrummedDown()) + HANDLE_EXPR(strummed_up, mStats.GetStrummedUp()) + HANDLE_EXPR(deploy_count, mStats.GetDeployCount()) + HANDLE_EXPR(solo_percentage, mStats.GetSoloPercentage()) + HANDLE_EXPR(perfect_solo_with_solo_buttons, mStats.GetPerfectSoloWithSoloButtons()) + HANDLE_EXPR(notes_per_streak, GetNotesPerStreak()) + HANDLE_EXPR(was_never_bad, mCrowd->GetMinValue() > mCrowd->GetThreshold(kExcitementBad)) + HANDLE_EXPR(stats_finalized, mStats.GetFinalized()) + HANDLE_ACTION(win, WinGame(_msg->Int(2))) + HANDLE_ACTION(lose, LoseGame()) + HANDLE_EXPR(score, GetScore()) + HANDLE_EXPR(accumulated_score, GetAccumulatedScore()) + HANDLE_EXPR(total_stars, GetTotalStars()) + HANDLE_EXPR(band, GetBand()) + HANDLE_EXPR(crowd_rating_active, mCrowd->IsActive()) + HANDLE_EXPR(crowd_rating, mCrowd->GetValue()) + HANDLE_EXPR(raw_crowd_rating, GetRawValue()) + HANDLE_EXPR(display_crowd_rating, GetDisplayValue()) + HANDLE_ACTION(set_crowd_rating_active, mCrowd->SetActive(_msg->Int(2))) + HANDLE_ACTION(set_crowd_rating, mCrowd->SetValue(_msg->Float(2))) + HANDLE_ACTION(remote_update_score, UpdateScore(_msg->Int(2))) + HANDLE_ACTION(remote_update_crowd, RemoteUpdateCrowd(_msg->Float(2))) + HANDLE_ACTION(send_remote_stats, SendRemoteStats(_msg->Obj(2))) + HANDLE_ACTION(remote_streak, SetRemoteStreak(_msg->Int(2))) + HANDLE_ACTION(remote_finished_song, RemoteFinishedSong(_msg->Int(2))) + HANDLE_ACTION(on_game_lost, SetLost()) + HANDLE_EXPR(get_multiplier_active, GetMultiplierActive()) + HANDLE_SUPERCLASS(Hmx::Object) + HANDLE_CHECK(0x24B) +END_HANDLERS +#pragma pop \ No newline at end of file diff --git a/src/band3/game/Performer.h b/src/band3/game/Performer.h index cf979acb..d07e223a 100644 --- a/src/band3/game/Performer.h +++ b/src/band3/game/Performer.h @@ -25,7 +25,7 @@ class Performer : public virtual Hmx::Object { virtual float GetNumStarsFloat() const = 0; virtual float GetTotalStars() const; virtual bool PastFinalNote() const = 0; - virtual int GetExcitement() const; + virtual ExcitementLevel GetExcitement() const; virtual void Poll(float, const SongPos&); virtual void AddPoints(float, bool, bool); virtual void Hit(); @@ -41,28 +41,54 @@ class Performer : public virtual Hmx::Object { virtual void ForceScore(int); virtual float GetNotesHitFraction(bool*) const = 0; virtual void SetQuarantined(bool); - virtual Symbol GetStreakType() const; + virtual Symbol GetStreakType() const { return "default"; } virtual float GetCrowdBoost() const; virtual void RemoteUpdateCrowd(float); - virtual int GetScoreForStars(int) const; + virtual int GetScoreForStars(int) const { return 0; } virtual void FinalizeStats(); - virtual bool CanStreak() const; + virtual bool CanStreak() const { return false; } - float unk8; - CrowdRating* unkc; - Stats unk10; - Band* unk1dc; + int GetIndividualScore() const; + int GetPercentComplete() const; + int GetSongNumVocalParts() const; + int GetNotesPerStreak() const; + void WinGame(int); + bool LoseGame(); + float GetRawValue() const; + float GetDisplayValue() const; + void UpdateScore(int); + void SendRemoteStats(BandUser*); + void SetRemoteStreak(int); + void RemoteFinishedSong(int); + void SetLost(); + bool GetMultiplierActive() const; + float PollMs() const; + void SetCrowdMeterActive(bool); + bool GetCrowdMeterActive(); + void SetStats(int, const Stats&); + void SendStreak(); + void TrulyWinGame(); + void ForceStars(int); + int GetNumRestarts() const; + void SetNoScorePercent(float); + bool IsLocal() const { return !IsNet(); } + Band* GetBand() const { return mBand; } + + float mPollMs; // 0x8 + CrowdRating* mCrowd; // 0xc + Stats mStats; // 0x10 + Band* mBand; // 0x1dc bool unk1e0; bool unk1e1; bool unk1e2; - float unk1e4; - SongPos unk1e8; + float mScore; // 0x1e4 + SongPos mSongPos; // 0x1e8 bool unk1fc; bool unk1fd; bool unk1fe; bool unk1ff; - float unk200; + float mProgressMs; // 0x200 bool unk204; - bool unk205; - int unk208; + bool mMultiplierActive; // 0x205 + int mNumRestarts; // 0x208 }; \ No newline at end of file diff --git a/src/band3/game/Scoring.cpp b/src/band3/game/Scoring.cpp index 86dba74f..882bfaa5 100644 --- a/src/band3/game/Scoring.cpp +++ b/src/band3/game/Scoring.cpp @@ -1,3 +1,135 @@ #include "Scoring.h" +#include "os/System.h" +#include "os/Debug.h" -Scoring TheScoring; \ No newline at end of file +Scoring* TheScoring; + +OverdriveConfig::OverdriveConfig(DataArray* cfg){ + rechargeRate = cfg->FindFloat("recharge_rate"); + starPhrase = cfg->FindFloat("star_phrase"); + commonPhrase = cfg->FindFloat("common_phrase"); + fillBoost = cfg->FindFloat("fill_boost"); + whammyRate = cfg->FindFloat("whammy_rate"); + readyLevel = cfg->FindFloat("ready_level"); + multiplier = cfg->FindInt("multiplier"); + crowdBoost = cfg->FindFloat("crowd_boost"); + MILO_ASSERT(( 0) <= (rechargeRate) && (rechargeRate) <= ( 1), 0x22); + MILO_ASSERT(( 0) <= (starPhrase) && (starPhrase) <= ( 1), 0x23); + MILO_ASSERT(( 0) <= (readyLevel) && (readyLevel) <= ( 1), 0x24); +} + +Scoring::Scoring() : unk8c(SystemConfig("scoring")), unk90(unk8c->FindArray("overdrive", true)), unkc0(0) { + MILO_ASSERT(!TheScoring, 0x2C); + TheScoring = this; + + unk8c->FindArray("points", true); + DataArray* streakarr = unk8c->FindArray("streaks", true); + DataArray* multarr = streakarr->FindArray("multipliers", true); + DataArray* energyarr = streakarr->FindArray("energy", true); + + unkb0 = unk8c->FindArray("unison_phrase", true)->FindFloat("reward"); + unkb4 = unk8c->FindArray("unison_phrase", true)->FindFloat("penalty"); + InitializeStreakList(unk78, multarr); + InitializeStreakList(unk80, energyarr); + + DataArray* pointarr = unk8c->FindArray("streaks", true); + for(int i = 1; i < pointarr->Size(); i++){ + int idx = SymToTrackType(pointarr->Array(i)->Sym(0)); + int head = pointarr->Array(i)->FindInt("head"); + int tail = pointarr->Array(i)->FindInt("tail"); + int chord = pointarr->Array(i)->FindInt("chord"); + PointInfo& info = mPointInfo[idx]; + info.headPoints = head; + info.tailPoints = tail; + info.chordPoints = chord; + } +} + +Scoring::~Scoring(){ + MILO_ASSERT(TheScoring, 0x48); + TheScoring = 0; +} + +int Scoring::GetHeadPoints(TrackType instrument) const { + MILO_ASSERT(instrument < kNumTrackTypes, 0xCA); + return mPointInfo[instrument].headPoints; +} + +int Scoring::GetTailPoints(TrackType instrument, int i) const { + MILO_ASSERT(instrument < kNumTrackTypes, 0xD0); + float f = ((float)i / 480.0f) * (float)mPointInfo[instrument].tailPoints; + if(f > 0) return f + 0.5f; + else return f - 0.5f; +} + +int Scoring::GetChordPoints(TrackType instrument) const { + MILO_ASSERT(instrument < kNumTrackTypes, 0xD7); + return mPointInfo[instrument].chordPoints; +} + +int Scoring::GetStreakMult(int i, Symbol s) const { + return GetStreakData(i, s, unk78); +} + +DataArray* Scoring::GetCrowdConfig(Difficulty diff, BandUser* user) const { + DataArray* diffarr = unk8c->FindArray("crowd", true)->FindArray(diff, true); + Symbol key = user ? user->GetTrackSym() : "default"; + DataArray* ret = diffarr->FindArray(key, false); + if(ret) return ret; + else return diffarr->FindArray("default", true); +} + +int Scoring::GetBandNumStars(int i) const { + int stars = GetNumStarsFloat(i, mStarThresholds); + if(stars < 0) return 0; + else if(stars > 6) return 6; + else return stars; +} + +float Scoring::GetBandNumStarsFloat(int i) const { + return GetNumStarsFloat(i, mStarThresholds); +} + +int Scoring::GetBandScoreForStars(int stars) const { + MILO_ASSERT(stars < mStarThresholds.size(), 0x18A); + return mStarThresholds[stars]; +} + +Symbol Scoring::GetStarRating(int numStars) const { + if(numStars == 0) return gNullStr; + else { + DataArray* ratings = unk8c->FindArray("star_ratings", "symbols")->Array(1); + MILO_ASSERT(( 1) <= (numStars) && (numStars) <= ( ratings->Size()), 0x1BE); + return ratings->Sym(numStars - 1); + } +} + +void Scoring::GetSoloAward(int i, Symbol s, int& iref, Symbol& sref){ + DataArray* soloblock = GetSoloBlock(s); + DataArray* awardarr = soloblock->FindArray("awards", true); + for(int idx = awardarr->Size() - 1; idx >= 1; idx--){ + DataArray* arr = awardarr->Array(idx); + if(i >= arr->Int(0)){ + iref = arr->Int(1); + sref = arr->Sym(2); + return; + } + } + MILO_FAIL("no solo award for %s: %i%%", s, i); +} + +DataArray* Scoring::GetSoloBlock(Symbol s) const { + DataArray* soloarr = unk8c->FindArray("solo", true); + DataArray* blockarr = soloarr->FindArray(s, false); + return blockarr ? blockarr : soloarr->FindArray("default", true); +} + +float Scoring::GetSoloGemReward(Symbol s){ + return GetSoloBlock(s)->FindFloat("reward"); +} + +float Scoring::GetSoloGemPenalty(Symbol s){ + return GetSoloBlock(s)->FindFloat("penalty"); +} + +void Scoring::PrintStarThresholds() const {} \ No newline at end of file diff --git a/src/band3/game/Scoring.h b/src/band3/game/Scoring.h index cac4b3f6..49701e44 100644 --- a/src/band3/game/Scoring.h +++ b/src/band3/game/Scoring.h @@ -1,14 +1,73 @@ #ifndef GAME_SCORING_H #define GAME_SCORING_H - #include "beatmatch/TrackType.h" +#include "game/Defines.h" +#include "game/BandUser.h" +#include "obj/Data.h" +#include + +class OverdriveConfig { +public: + OverdriveConfig(DataArray*); + + float rechargeRate; // 0x0 + float starPhrase; // 0x4 + float commonPhrase; // 0x8 + float fillBoost; // 0xc + float whammyRate; // 0x10 + float readyLevel; // 0x14 + int multiplier; // 0x18 + float crowdBoost; // 0x1c +}; + +// idk the name +class PointInfo { +public: + PointInfo(int x = 0, int y = 0, int z = 0) : headPoints(x), tailPoints(y), chordPoints(z) {} + int headPoints; // 0x0 + int tailPoints; // 0x4 + int chordPoints; // 0x8 +}; class Scoring { public: + class StreakList { + public: + }; + + Scoring(); + ~Scoring(); + + PointInfo mPointInfo[10]; // 0xc + std::vector unk78; // 0x78 + std::vector unk80; // 0x80 + int unk88; + DataArray* unk8c; + OverdriveConfig unk90; // 0x90 + float unkb0; // 0xb0 + float unkb4; // 0xb4 + mutable std::vector mStarThresholds; // 0xb8 + int unkc0; // 0xc0 + int GetHeadPoints(TrackType) const; int GetTailPoints(TrackType, int) const; + int GetChordPoints(TrackType) const; + void InitializeStreakList(std::vector&, DataArray*); + DataArray* GetCrowdConfig(Difficulty, BandUser*) const; + float GetStreakData(int, Symbol, const std::vector&) const; + int GetStreakMult(int, Symbol) const; + int GetBandNumStars(int) const; + float GetBandNumStarsFloat(int) const; + float GetNumStarsFloat(int, std::vector&) const; + int GetBandScoreForStars(int) const; + Symbol GetStarRating(int) const; + DataArray* GetSoloBlock(Symbol) const; + void GetSoloAward(int, Symbol, int&, Symbol&); + float GetSoloGemReward(Symbol); + float GetSoloGemPenalty(Symbol); + void PrintStarThresholds() const; }; -extern Scoring TheScoring; +extern Scoring* TheScoring; #endif // GAME_SCORING_H \ No newline at end of file diff --git a/src/band3/game/SongDB.cpp b/src/band3/game/SongDB.cpp new file mode 100644 index 00000000..de4084c1 --- /dev/null +++ b/src/band3/game/SongDB.cpp @@ -0,0 +1,90 @@ +#include "game/SongDB.h" + +SongDB::SongDB() : mSongData(new SongData()), mSongDurationMs(0), mCodaStartTick(-1), mMultiplayerAnalyzer(new MultiplayerAnalyzer(mSongData)), unk24(-1), unk28(-1) { + mSongData->AddSink(this); +} + +SongDB::~SongDB(){ + RELEASE(mSongData); +} + +void SongDB::PostLoad(DataEventList* list){ + ParseEvents(list); + SpewAllVocalNotes(); + SpewTrackSizes(); + SetupPhrases(); + DisableCodaGems(); + RunMultiplayerAnalyzer(); + SetupPracticeSections(); +} + +void SongDB::RunMultiplayerAnalyzer(){ + +} + +void SongDB::RebuildPhrases(int i){ + ClearTrackPhrases(i); + mSongData->SendPhrases(i); +} + +void SongDB::RebuildData(){ + SetupPhrases(); + RunMultiplayerAnalyzer(); +} + +void SongDB::OverrideBasePoints(int i, TrackType ty, const UserGuid& guid, int i1, int i2, int i3){ + mMultiplayerAnalyzer->OverrideBasePoints(i, ty, guid, i1, i2, i3); +} + +int SongDB::TotalBasePoints(){ return mMultiplayerAnalyzer->TotalBasePoints(); } +float SongDB::GetSongDurationMs() const { return mSongDurationMs; } +int SongDB::GetCodaStartTick() const { return mCodaStartTick; } + +bool SongDB::IsInCoda(int i) const { + return mCodaStartTick != -1 && i >= mCodaStartTick; +} + +int SongDB::GetNumTracks() const { return mSongData->unk10; } +int SongDB::GetNumTrackData() const { return mTrackData.size(); } + +int SongDB::GetBaseMaxPoints(const UserGuid& u) const { + return mMultiplayerAnalyzer->GetMaxPoints(u); +} + +int SongDB::GetBaseMaxStreakPoints(const UserGuid& u) const { + return mMultiplayerAnalyzer->GetMaxStreakPoints(u); +} + +int SongDB::GetBaseBonusPoints(const UserGuid& u) const { + return mMultiplayerAnalyzer->GetBonusPoints(u); +} + +GameGemList* SongDB::GetGemList(int i) const { + return mSongData->GetGemList(i); +} + +GameGemList* SongDB::GetGemListByDiff(int i, int j) const { + return mSongData->GetGemListByDiff(i, j); +} + +const std::vector& SongDB::GetGems(int i) const { + return mSongData->GetGemList(i)->mGems; +} + +#pragma push +#pragma dont_inline on +void SongDB::AddTrack(int, Symbol, SongInfoAudioType, TrackType ty, bool){ + mTrackData.push_back(ty); +} +#pragma pop + +std::vector& SongDB::GetRangeSections(){ return mSongData->mRangeSections; } + +void SongDB::ChangeDifficulty(int i, Difficulty diff){ + mSongData->ChangeTrackDiff(i, diff); +} + +void SongDB::SetTrainerGems(int i, int j){ + unk24 = i; + unk28 = j; +} \ No newline at end of file diff --git a/src/band3/game/SongDB.h b/src/band3/game/SongDB.h index f503a668..c7da3a62 100644 --- a/src/band3/game/SongDB.h +++ b/src/band3/game/SongDB.h @@ -1,32 +1,74 @@ #ifndef GAME_SONGDB_H #define GAME_SONGDB_H - #include "system/beatmatch/SongData.h" #include "system/beatmatch/SongParserSink.h" +#include "game/MultiplayerAnalyzer.h" +#include "game/Defines.h" +#include "midi/DataEvent.h" #include class SongDB : public SongParserSink { public: + class TrackData { + public: + TrackData(TrackType ty) : unk0(ty) {} + + TrackType unk0; + std::vector unk4; + std::vector unkc; + std::vector unk14; + std::vector unk1c; + std::vector unk24; + std::vector unk2c; + std::vector unk34; + std::vector unk3c; + }; + SongDB(); virtual ~SongDB(); - virtual void SetNumTracks(int); virtual void AddTrack(int, Symbol, SongInfoAudioType, TrackType, bool); - virtual void AddMultiGem(int, const GameGem&); + virtual void AddMultiGem(int, const GameGem&){} virtual void AddPhrase(BeatmatchPhraseType, int, const Phrase&); - float GetSongDurationMs(); + float GetSongDurationMs() const; + void ParseEvents(DataEventList*); + void SpewAllVocalNotes() const; + void SpewTrackSizes() const; + void SetupPhrases(); + void DisableCodaGems(); + void RunMultiplayerAnalyzer(); + void SetupPracticeSections(); + void PostLoad(DataEventList*); + void RebuildPhrases(int); + void ClearTrackPhrases(int); + void RebuildData(); + void OverrideBasePoints(int, TrackType, const UserGuid&, int, int, int); + int TotalBasePoints(); + int GetCodaStartTick() const; + bool IsInCoda(int) const; + int GetNumTracks() const; + int GetNumTrackData() const; + int GetBaseMaxPoints(const UserGuid&) const; + int GetBaseMaxStreakPoints(const UserGuid&) const; + int GetBaseBonusPoints(const UserGuid&) const; + GameGemList* GetGemList(int) const; + GameGemList* GetGemListByDiff(int, int) const; + const std::vector& GetGems(int) const; + std::vector& GetRangeSections(); + void ChangeDifficulty(int, Difficulty); + void SetTrainerGems(int, int); SongData* GetSongData() { return mSongData; } - SongData* mSongData; - std::vector mTrackData; // todo: type - float mSongDurationMs; - int mCodaStartTick; - int mMultiplayerAnalyzer; // MultiplayerAnalyzer* - std::vector mPracticeSections; // todo: type - int mUnk1; - int mUnk2; + SongData* mSongData; // 0x4 + std::vector mTrackData; // 0x8 + float mSongDurationMs; // 0x10 + int mCodaStartTick; // 0x14 + MultiplayerAnalyzer* mMultiplayerAnalyzer; // 0x18 + std::vector mPracticeSections; // 0x1c + int unk24; // 0x24 + int unk28; // 0x28 }; extern SongDB* TheSongDB; diff --git a/src/band3/game/Stats.cpp b/src/band3/game/Stats.cpp index f0a37092..6700c592 100644 --- a/src/band3/game/Stats.cpp +++ b/src/band3/game/Stats.cpp @@ -1,65 +1,229 @@ #include "Stats.h" - -Stats::Stats() { +#include "math/MathFuncs.h" +#include "os/Debug.h" + +Stats::Stats() : mHitCount(0), mMissCount(0), m0x08(0), m0x0c(0), mPersistentStreak(0), mLongestPersistentStreak(0), mNotesHitFraction(0), mFailedDeploy(0), mDeployCount(0), mFillHitCount(0), m0x28(0), m0x2c(0), + m0x30(0), m0x34(0), mFinalized(0), mSoloPercentage(0), mSoloButtonedSoloPercentage(0), mPerfectSoloWithSoloButtons(0), m0x41(0), mNumberOfSingers(0), m0x48(0), mDoubleHarmonyHit(0), mDoubleHarmonyPhraseCount(0), + mTripleHarmonyHit(0), mTripleHarmonyPhraseCount(0), m0x5c(0), m0x60(0), m0x64(0), m0x68(0), m0x6c(0), mAccuracy(0), m0x8c(0), mSolo(0), mOverdrive(0), mSustain(0), mScoreStreak(0), + mBandContribution(0), mCodaPoints(0), m0xa8(0), m0x09(0), mTambourine(0), mHarmony(0), m0xb4(0), mNoScorePercent(0), mHitStreaks(3), mMissStreaks(3), unkdc(3, -1.0f), unke4(3, -1.0f), mPlayersSaved(0), + unkf0(3, 2.0f), mTimesSaved(0), unkfc(3, 2.0f), unk104(3, -1), mBestOverdriveDeployments(3), mTotalOverdriveDurationMs(0), mBestStreakMultipliers(3), mTotalMultiplierDuration(0), m0x14c(0), m0x150(0), mEndGameScore(0), + mEndGameCrowdLevel(0), mEndGameOverdrive(0), mOverdrivePhrasesCompleted(0), mOverdrivePhraseCount(0), mUnisonPhraseCompleted(0), mUnisonPhraseCount(0), mHopoGemInfo1(0), mHopoGemInfo2(0), + mHopoGemInfo3(0), mHighGemsHitHigh(0), mHighGemsHitLow(0), mHighFretGemCount(0), mSustainGemsHitCompletely(0), mSustainGemsHitPartially(0), mSustainGemCount(0), m0x194(0), mRollCount(0), + mRollsHitCompletely(0), mTrillCount(0), mTrillsHitCompletely(0), mTrillsHitPartially(0), mCymbalGemInfo1(0), mCymbalGemInfo2(0), mCymbalGemInfo3(0), unk1c0(0), unk1c4(0), unk1c8(0) { } -void Stats::BuildHitStreak(int, float) { - +void Stats::BuildHitStreak(int i, float f) { + mHitCount++; + unk1c8 += f; + BuildStreak(mCurrentHitStreak, i); + mPersistentStreak++; + mLongestPersistentStreak = Max(mLongestPersistentStreak, mPersistentStreak); } int Stats::GetCurrentStreak() const { - return mCurrentStreak; + return mCurrentHitStreak.mDuration; } void Stats::SetCurrentStreak(int currentStreak) { - mCurrentStreak = currentStreak; + mCurrentHitStreak.mDuration = currentStreak; } -void Stats::GetLongestStreak() const { - +int Stats::GetLongestStreak() const { + return mHitStreaks[0].mDuration; } -void Stats::SetPersistentStreak(int) { - +void Stats::SetPersistentStreak(int i) { + mPersistentStreak = i; + mLongestPersistentStreak = Max(mLongestPersistentStreak, i); } void Stats::EndHitStreak() { - + EndStreak(mCurrentHitStreak, mHitStreaks); + mPersistentStreak = 0; } -void Stats::BuildMissStreak(int) { - +void Stats::BuildMissStreak(int i) { + mMissCount++; + BuildStreak(mCurrentMissStreak, i); } void Stats::EndMissStreak() { + EndStreak(mCurrentMissStreak, mMissStreaks); +} + +void Stats::BuildStreak(Stats::StreakInfo& info, int i) { + if(info.mDuration == 0) info.mStart = i; + info.mDuration++; +} +void Stats::EndStreak(Stats::StreakInfo& info, std::vector& vec) { + if(info.mDuration > 0) SaveHighest(vec, info); + info = StreakInfo(); } -void Stats::BuildStreak(Stats::StreakInfo&, int) { +template +void Stats::SaveHighest(std::vector& vec, const T& item){ + std::vector::iterator it; + for(it = vec.begin(); it != vec.end(); ++it){ + if(*it > item){ + vec.pop_back(); + vec.insert(it, item); + break; + } + } +} +void Stats::SetFinalized(bool b){ + mFinalized = b; + if(b){ + for(int i = 0; i < mSingerStats.size(); i++){ + mSingerStats[i].Finalize(); + } + } } -void Stats::EndStreak(Stats::StreakInfo&, std::vector&) { +void Stats::UpdateBestSolo(int i){ + int item = i; + SaveHighest(unk104, item); + int& asdf = unk104[0]; + MaxEq(asdf, 0); + mSoloPercentage = asdf; +} +void Stats::SetSoloButtonedSoloPercentage(int i){ + if(mSoloButtonedSoloPercentage < i) mSoloButtonedSoloPercentage = i; } -void Stats::SetFinalized(bool) {} -void Stats::UpdateBestSolo(int) {} -void Stats::SetSoloButtonedSoloPercentage(int) {} void Stats::SetVocalSingerAndPartCounts(int, int) {} -void Stats::SetSingerPartPercentage(int, int, float) {} +void Stats::SetSingerPartPercentage(int i, int j, float f){ + mSingerStats[i].SetPartPercentage(j, f); +} + +float Stats::GetSingerRankedPercentage(int i, int j) const { + return mSingerStats[i].unk0[j].second; +} + +int Stats::GetSingerRankedPart(int i, int j) const { + return mSingerStats[i].unk0[j].first; +} + +void Stats::SetSingerPitchDeviationInfo(int i, float f1, float f2){ + mSingerStats[i].SetPitchDeviationInfo(f1, f2); +} + +void Stats::UpdateBestTambourineSection(int i){ + if(m0x5c < i) m0x5c = i; +} + +void Stats::SaveForEndGame(BinStream& bs) const { + bs << mHitCount; + bs << mHitStreaks; + bs << mPersistentStreak; + bs << mLongestPersistentStreak; + bs << mNotesHitFraction; + bs << m0x194; + bs << mFailedDeploy; + bs << mPlayersSaved; + bs << mFillHitCount; + bs << m0x2c; + bs << m0x28; + bs << mDeployCount; + bs << mSoloPercentage; + bs << mDoubleHarmonyHit; + bs << mDoubleHarmonyPhraseCount; + bs << mTripleHarmonyHit; + bs << mTripleHarmonyPhraseCount; + bs << mNumberOfSingers; + bs << m0x48; + bs << mAccuracy; + bs << m0x8c; + bs << mOverdrive; + bs << mSolo; + bs << mSustain; + bs << mScoreStreak; + bs << mBandContribution; + bs << mCodaPoints; + bs << m0xa8; + bs << m0x09; + bs << mOverdrivePhrasesCompleted; + bs << mOverdrivePhraseCount; + bs << mUnisonPhraseCompleted; + bs << mUnisonPhraseCount; + bs << mTambourine; + bs << mHarmony; + bs << m0xb4; + bs << mNoScorePercent; + bs << mSections; + bs << unk1c0; + bs << unk1c4; + bs << unk1c8; + SaveSingerStats(bs); +} + +void Stats::LoadForEndGame(BinStream& bs){ + bs >> mHitCount; + bs >> mHitStreaks; + bs >> mPersistentStreak; + bs >> mLongestPersistentStreak; + bs >> mNotesHitFraction; + bs >> m0x194; + bs >> mFailedDeploy; + bs >> mPlayersSaved; + bs >> mFillHitCount; + bs >> m0x2c; + bs >> m0x28; + bs >> mDeployCount; + bs >> mSoloPercentage; + bs >> mDoubleHarmonyHit; + bs >> mDoubleHarmonyPhraseCount; + bs >> mTripleHarmonyHit; + bs >> mTripleHarmonyPhraseCount; + bs >> mNumberOfSingers; + bs >> m0x48; + bs >> mAccuracy; + bs >> m0x8c; + bs >> mOverdrive; + bs >> mSolo; + bs >> mSustain; + bs >> mScoreStreak; + bs >> mBandContribution; + bs >> mCodaPoints; + bs >> m0xa8; + bs >> m0x09; + bs >> mOverdrivePhrasesCompleted; + bs >> mOverdrivePhraseCount; + bs >> mUnisonPhraseCompleted; + bs >> mUnisonPhraseCount; + bs >> mTambourine; + bs >> mHarmony; + bs >> m0xb4; + bs >> mNoScorePercent; + bs >> mSections; + bs >> unk1c0; + bs >> unk1c4; + bs >> unk1c8; + LoadSingerStats(bs); +} -void Stats::GetSingerRankedPercentage(int, int) const {} +void Stats::SaveSingerStats(BinStream& bs) const { + for(int i = 0; i < mNumberOfSingers; i++){ + const SingerStats& stats = mSingerStats[i]; + for(int j = 0; j < m0x48; j++){ + const std::pair& p = stats.unk0[j]; + bs << p.first; + bs << p.second; + } + float f1, f2; + stats.GetPitchDeviationInfo(f1, f2); + bs << f1; + bs << f2; + } +} -void Stats::GetSingerRankedPart(int, int) const {} +void Stats::LoadSingerStats(BinStream& bs){ -void Stats::SetSingerPitchDeviationInfo(int, float, float) {} -void Stats::UpdateBestTambourineSection(int) {} -void Stats::SaveForEndGame(BinStream&) const {} -void Stats::LoadForEndGame(BinStream&) {} -void Stats::SaveSingerStats(BinStream&) const {} -void Stats::LoadSingerStats(BinStream&) {} +} void Stats::AddAccuracy(int accuracy) { mAccuracy += accuracy; } void Stats::AddOverdrive(float overdrive) { mOverdrive += overdrive;} @@ -70,7 +234,6 @@ void Stats::AddBandContribution(float bandContribution ) { mBandContribution += void Stats::AddCodaPoints(int codaPoints) { mCodaPoints += codaPoints; } void Stats::AddTambourine(float tambourine) { mTambourine += tambourine; } void Stats::AddHarmony(int harmony) { mHarmony += harmony; } - int Stats::GetAccuracy() const { return mAccuracy; } float Stats::GetOverdrive() const { return mOverdrive; } int Stats::GetSolo() const { return mSolo; } @@ -82,28 +245,89 @@ float Stats::GetTambourine() const { return mTambourine; } int Stats::GetHarmony() const { return mHarmony; } void Stats::SetNoScorePercent(float) {} -void Stats::FailedNoScore() const {} +bool Stats::FailedNoScore() const {} void Stats::AddFailurePoint(float) {} void Stats::AddToPlayersSaved(int, float) {} void Stats::AddToTimesSaved(float, float) {} -void Stats::DeployOverdrive(float, int) {} -void Stats::StopDeployingOverdrive(float, int) {} -void Stats::BeginStreakMultiplier(float, int) {} -void Stats::EndStreakMultiplier(float, int) {} -void Stats::BeginMultiplier(Stats::MultiplierInfo&, float, int, float) {} -void Stats::EndMultiplier(Stats::MultiplierInfo&, std::vector&, float, int, float, float&) {} + +void Stats::DeployOverdrive(float f, int i){ + BeginMultiplier(mCurrentOverdriveDeployment, f, i, mOverdrive); +} + +void Stats::StopDeployingOverdrive(float f, int i){ + EndMultiplier(mCurrentOverdriveDeployment, mBestOverdriveDeployments, f, i, mOverdrive, mTotalOverdriveDurationMs); +} + +void Stats::BeginStreakMultiplier(float f, int i){ + BeginMultiplier(mCurrentStreakMultiplier, f, i, mScoreStreak); +} + +void Stats::EndStreakMultiplier(float f, int i){ + EndMultiplier(mCurrentStreakMultiplier, mBestStreakMultipliers, f, i, mScoreStreak, mTotalMultiplierDuration); +} + +void Stats::BeginMultiplier(Stats::MultiplierInfo& info, float f1, int i, float f2){ + info.mStartMs = f1; + info.mStartingMultiplier = i; + info.mPoints = f2; +} + +void Stats::EndMultiplier(Stats::MultiplierInfo& info, std::vector& vec, float f1, int i, float f2, float& fref){ + if(info.mStartMs < 0) return; + info.mEndingMultiplier = i; + info.mDurationMs = f1 - info.mStartMs; + info.mPoints = f2 - info.mPoints; + SaveHighest(vec, info); + fref += info.mDurationMs; + info = MultiplierInfo(); +} void Stats::GetUnisonPhrasePercent() const {} -void Stats::SetHopoGemInfo(int, int, int) {} -void Stats::IncrementHighFretGemsHit(bool) {} -void Stats::IncrementSustainGemsHit(bool) {} -void Stats::AddRoll(bool) {} -void Stats::IncrementTrillsHit(bool) {} -void Stats::SetCymbalGemInfo(int, int, int) {} -void Stats::SetSectionInfo(int, Symbol, float, float) {} +void Stats::SetHopoGemInfo(int i1, int i2, int i3){ + mHopoGemInfo1 = i1; + mHopoGemInfo2 = i2; + mHopoGemInfo3 = i3; +} + +void Stats::IncrementHighFretGemsHit(bool b){ + if(b) mHighGemsHitHigh++; + else mHighGemsHitLow++; +} + +void Stats::IncrementSustainGemsHit(bool b){ + if(b) mSustainGemsHitCompletely++; + else mSustainGemsHitPartially++; +} + +void Stats::AddRoll(bool b){ + if(b) mRollsHitCompletely++; + mRollCount++; +} + +void Stats::IncrementTrillsHit(bool b){ + if(b) mTrillsHitCompletely++; + else mTrillsHitPartially++; +} -void Stats::GetSectionInfo(int) const {} +void Stats::SetCymbalGemInfo(int i1, int i2, int i3){ + mCymbalGemInfo1 = i3; + mCymbalGemInfo2 = i1; + mCymbalGemInfo3 = i2; +} + +void Stats::SetSectionInfo(int index, Symbol s, float f1, float f2){ + MILO_ASSERT(index < mSections.size(), 0x36F); + SectionInfo& info = mSections[index]; + info.unk0 = s; + info.unk4 = f1; + info.unk8 = f2; +} + +const Stats::SectionInfo& Stats::GetSectionInfo(int index) const { + MILO_ASSERT(index < mSections.size(), 0x378); + return mSections[index]; +} void Stats::GetAverageMsError() const {} @@ -120,6 +344,41 @@ void SingerStats::SetPitchDeviationInfo(float param_1, float param_2) { void SingerStats::GetPitchDeviationInfo(float&, float&) const {} -Stats::StreakInfo::StreakInfo() {} -Stats::MultiplierInfo::MultiplierInfo() {} -Stats::SectionInfo::SectionInfo() {} +Stats::StreakInfo::StreakInfo() : mStart(-1), mDuration(0) {} + +BinStream& operator<<(BinStream& bs, const Stats::StreakInfo& info){ + bs << info.mStart; + bs << info.mDuration; + return bs; +} + +BinStream& operator>>(BinStream& bs, Stats::StreakInfo& info){ + bs >> info.mStart; + bs >> info.mDuration; + return bs; +} + +Stats::MultiplierInfo::MultiplierInfo() : mStartMs(-1.0f), mDurationMs(0), mStartingMultiplier(0), mEndingMultiplier(0), mPoints(0) {} + +bool operator>(const Stats::MultiplierInfo& info1, const Stats::MultiplierInfo& info2){ + if(info1.mPoints == info2.mPoints){ + return info1.mDurationMs < info2.mDurationMs; + } + else return info1.mPoints < info2.mPoints; +} + +Stats::SectionInfo::SectionInfo() : unk4(-1.0f), unk8(0) {} + +BinStream& operator<<(BinStream& bs, const Stats::SectionInfo& info){ + bs << info.unk0; + bs << info.unk4; + bs << info.unk8; + return bs; +} + +BinStream& operator>>(BinStream& bs, Stats::SectionInfo& info){ + bs >> info.unk0; + bs >> info.unk4; + bs >> info.unk8; + return bs; +} \ No newline at end of file diff --git a/src/band3/game/Stats.h b/src/band3/game/Stats.h index 95e50720..3dbe6345 100644 --- a/src/band3/game/Stats.h +++ b/src/band3/game/Stats.h @@ -1,31 +1,76 @@ #ifndef GAME_STATS_H #define GAME_STATS_H - #include #include "system/utl/BinStream.h" +class SingerStats { +public: + SingerStats(int); + void Finalize(); + void SetPartPercentage(int, float); + void GetRankData(int) const; + void SetPitchDeviationInfo(float, float); + void GetPitchDeviationInfo(float&, float&) const; + + std::vector > unk0; // 0x0 + float mPitchDeviation1; // 0x08 + float mPitchDeviation2; // 0x0c + +}; + class Stats { +public: class StreakInfo { + public: StreakInfo(); + bool operator>(const StreakInfo& s){ + return mDuration > s.mDuration; + } + + int mStart; // 0x0 + int mDuration; // 0x4 }; class MultiplierInfo { + public: MultiplierInfo(); + + float mStartMs; // 0x0 + float mDurationMs; // 0x4 + int mStartingMultiplier; // 0x8 + int mEndingMultiplier; // 0xc + float mPoints; // 0x10 }; class SectionInfo { + public: SectionInfo(); + + Symbol unk0; // 0x0 + float unk4; // 0x4 + float unk8; // 0x8 }; -public: Stats(); - Stats(const Stats&); - ~Stats(); + Stats(const Stats& s); + + + // Stats::Stats(const Stats& s) : mHitCount(s.mHitCount), mMissCount(s.mMissCount), m0x08(s.m0x08), m0x0c(s.m0x0c), mPersistentStreak(s.mPersistentStreak), mLongestPersistentStreak(s.mLongestPersistentStreak), + // mNotesHitFraction(s.mNotesHitFraction), mFailedDeploy(s.mFailedDeploy), mDeployCount(s.mDeployCount), mFillHitCount(s.mFillHitCount), m0x28(s.m0x28), m0x2c(s.m0x2c), m0x30(s.m0x30), m0x34(s.m0x34), + // mFinalized(s.mFinalized), mSoloPercentage(s.mSoloPercentage), mSoloButtonedSoloPercentage(s.mSoloButtonedSoloPercentage), mPerfectSoloWithSoloButtons(s.mPerfectSoloWithSoloButtons), m0x41(s.m0x41), + // mNumberOfSingers(s.mNumberOfSingers), m0x48(s.m0x48), mDoubleHarmonyHit(s.mDoubleHarmonyHit), mDoubleHarmonyPhraseCount(s.mDoubleHarmonyPhraseCount), mTripleHarmonyHit(s.mTripleHarmonyHit), + // mTripleHarmonyPhraseCount(s.mTripleHarmonyPhraseCount), m0x5c(s.m0x5c), m0x60(s.m0x60), m0x64(s.m0x64), m0x68(s.m0x68), m0x6c(s.m0x6c), m0x70(s.m0x70), mSingerStats(s.mSingerStats), m0x80(s.m0x80), + // mAccuracy(s.mAccuracy), m0x8c(s.m0x8c), mSolo(s.mSolo), mOverdrive(s.mOverdrive), mSustain(s.mSustain), mScoreStreak(s.mScoreStreak), mBandContribution(s.mBandContribution), + // mCodaPoints(s.mCodaPoints), m0xa8(s.m0xa8), m0x09(s.m0x09), mTambourine(s.mTambourine), mHarmony(s.mHarmony), m0xb4(s.m0xb4), mNoScorePercent(s.mNoScorePercent), mCurrentHitStreak(s.mCurrentHitStreak) { + + // } + + ~Stats(){} void BuildHitStreak(int, float); int GetCurrentStreak() const; void SetCurrentStreak(int); - void GetLongestStreak() const; + int GetLongestStreak() const; void SetPersistentStreak(int); void EndHitStreak(); void BuildMissStreak(int); @@ -37,8 +82,8 @@ class Stats { void SetSoloButtonedSoloPercentage(int); void SetVocalSingerAndPartCounts(int, int); void SetSingerPartPercentage(int, int, float); - void GetSingerRankedPercentage(int, int) const; - void GetSingerRankedPart(int, int) const; + float GetSingerRankedPercentage(int, int) const; + int GetSingerRankedPart(int, int) const; void SetSingerPitchDeviationInfo(int, float, float); void UpdateBestTambourineSection(int); void SaveForEndGame(BinStream&) const; @@ -64,7 +109,7 @@ class Stats { float GetTambourine() const; int GetHarmony() const; void SetNoScorePercent(float); - void FailedNoScore() const; + bool FailedNoScore() const; void AddFailurePoint(float); void AddToPlayersSaved(int, float); void AddToTimesSaved(float, float); @@ -82,28 +127,26 @@ class Stats { void IncrementTrillsHit(bool); void SetCymbalGemInfo(int, int, int); void SetSectionInfo(int, Symbol, float, float); - void GetSectionInfo(int) const; + const SectionInfo& GetSectionInfo(int) const; void GetAverageMsError() const; - - // These are implemented in Performer - int GetDoubleHarmonyHit() const; - int GetDoubleHarmonyPhraseCount() const; - int GetTripleHarmonyHit() const; - int GetTripleHarmonyPhraseCount() const; - int GetHitCount() const; - float GetNotesHitFraction() const; - int GetNumberOfSingers() const; - void GetVocalPartPercentage(int) const; - bool GetFailedDeploy() const; - int GetPlayersSaved() const; - int GetFillHitCount() const; - void GetStrummedDown() const; - void GetStrummedUp() const; - int GetDeployCount() const; - int GetSoloPercentage() const; - bool GetPerfectSoloWithSoloButtons() const; - bool GetFinalized() const; + int GetDoubleHarmonyHit() const { return mDoubleHarmonyHit; } + int GetDoubleHarmonyPhraseCount() const { return mDoubleHarmonyPhraseCount; } + int GetTripleHarmonyHit() const { return mTripleHarmonyHit; } + int GetTripleHarmonyPhraseCount() const { return mTripleHarmonyPhraseCount; } + int GetHitCount() const { return mHitCount; } + float GetNotesHitFraction() const { return mNotesHitFraction; } + int GetNumberOfSingers() const { return mNumberOfSingers; } + float GetVocalPartPercentage(int i) const { return m0x70[i]; } + bool GetFailedDeploy() const { return mFailedDeploy; } + int GetPlayersSaved() const { return mPlayersSaved; } + int GetFillHitCount() const { return mFillHitCount; } + bool GetStrummedDown() const { return m0x2c > 0; } + bool GetStrummedUp() const { return m0x28 > 0; } + int GetDeployCount() const { return mDeployCount; } + int GetSoloPercentage() const { return mSoloPercentage; } + bool GetPerfectSoloWithSoloButtons() const { return mPerfectSoloWithSoloButtons; } + bool GetFinalized() const { return mFinalized; } // These are implemented in PerformanceData void AccessPerformanceAwards(); @@ -152,13 +195,15 @@ class Stats { void SetTripleHarmonyHit(int); void SetTripleHarmonyPhraseCount(int); void AccessSingerStats(int); + + template void SaveHighest(std::vector&, const T&); int mHitCount; // 0x000 int mMissCount; // 0x004 int m0x08; // 0x008 int m0x0c; // 0x00c - int m0x10; // 0x010 - int m0x14; // 0x014 + int mPersistentStreak; // 0x010 + int mLongestPersistentStreak; // 0x014 float mNotesHitFraction; // 0x018 bool mFailedDeploy; // 0x01c int mDeployCount; // 0x020 @@ -169,7 +214,7 @@ class Stats { bool m0x34; // 0x034 bool mFinalized; // 0x035 int mSoloPercentage; // 0x038 - int m0x3c; // 0x03c + int mSoloButtonedSoloPercentage; // 0x03c bool mPerfectSoloWithSoloButtons; // 0x040 bool m0x41; // 0x041 int mNumberOfSingers; // 0x044 @@ -183,9 +228,9 @@ class Stats { int m0x64; // 0x064 int m0x68; // 0x068 int m0x6c; // 0x06c - std::vector m0x70; // 0x070 - std::vector m0x78; // 0x078 - std::vector m0x80; // 0x080 + std::vector m0x70; // 0x070 + std::vector mSingerStats; // 0x078 + std::vector m0x80; // 0x080 int mAccuracy; // 0x88 int m0x8c; int mSolo; // 0x90 @@ -199,44 +244,25 @@ class Stats { float mTambourine; // 0xac int mHarmony; // 0xb0 bool m0xb4; - int m0xb8; - int m0xbc; - int mCurrentStreak; // 0xc0 - int m0xc4; - int m0xc8; - int m0xcc; - int m0xd0; - int m0xd4; - int m0xd8; - int m0xdc; - int m0xe0; - int m0xe4; - int m0xe8; + float mNoScorePercent; // 0xb8 + StreakInfo mCurrentHitStreak; // 0xbc + std::vector mHitStreaks; // 0xc4 + StreakInfo mCurrentMissStreak; // 0xcc + std::vector mMissStreaks; // 0xd4 + std::vector unkdc; // 0xdc + std::vector unke4; // 0xe4 int mPlayersSaved; // 0xec - int m0xf0; - int m0xf4; + std::vector unkf0; // 0xf0 int mTimesSaved; // 0xf8 - int m0xfc; - int m0x100; - int m0x104; - int m0x108; - int m0x10c; - int m0x110; - int m0x114; - int m0x118; - int m0x11c; - int m0x120; - int m0x124; - float mTotalOverdriveDuration; // 0x128 - int m0x12c; - int m0x130; - int m0x134; - int m0x138; - int m0x13c; - int m0x140; - int m0x144; + std::vector unkfc; // 0xfc + std::vector unk104; // 0x104 + MultiplierInfo mCurrentOverdriveDeployment; // 0x10c + std::vector mBestOverdriveDeployments; // 0x120 + float mTotalOverdriveDurationMs; // 0x128 + MultiplierInfo mCurrentStreakMultiplier; // 0x12c + std::vector mBestStreakMultipliers; // 0x140 float mTotalMultiplierDuration; // 0x148 - int m0x14c; + int m0x14c; // 0x14c int m0x150; int mEndGameScore; // 0x154 float mEndGameCrowdLevel; // 0x158 @@ -260,28 +286,21 @@ class Stats { int mTrillCount; // 0x1a0 int mTrillsHitCompletely; // 0x1a4 int mTrillsHitPartially; // 0x1a8 - int mSymbolGemInfo1; // 0x1ac - int mSymbolGemInfo2; // 0x1b0 - int mSymbolGemInfo3; // 0x1b4 - std::vector unk1b8; // 0x1b8 + int mCymbalGemInfo1; // 0x1ac + int mCymbalGemInfo2; // 0x1b0 + int mCymbalGemInfo3; // 0x1b4 + std::vector mSections; // 0x1b8 float unk1c0; float unk1c4; - // float unk1c8; + float unk1c8; - bool mMultiplierActive; // 0x205 + // bool mMultiplierActive; // 0x205 }; -class SingerStats { - SingerStats(int); - void Finalize(); - void SetPartPercentage(int, float); - void GetRankData(int) const; - void SetPitchDeviationInfo(float, float); - void GetPitchDeviationInfo(float&, float&) const; - - float mPitchDeviation1; // 0x08 - float mPitchDeviation2; // 0x0c - -}; +BinStream& operator<<(BinStream&, const Stats::StreakInfo&); +BinStream& operator>>(BinStream&, Stats::StreakInfo&); +BinStream& operator<<(BinStream&, const Stats::SectionInfo&); +BinStream& operator>>(BinStream&, Stats::SectionInfo&); +bool operator>(const Stats::MultiplierInfo&, const Stats::MultiplierInfo&); #endif // GAME_STATS_H diff --git a/src/band3/meta_band/BandSongMetadata.cpp b/src/band3/meta_band/BandSongMetadata.cpp index 87c0463c..2101ea73 100644 --- a/src/band3/meta_band/BandSongMetadata.cpp +++ b/src/band3/meta_band/BandSongMetadata.cpp @@ -233,15 +233,15 @@ bool BandSongMetadata::HasPart(Symbol, bool) const { float BandSongMetadata::Rank(Symbol s) const { if(s == real_guitar || s == real_bass){ SongUpgradeData* data = mSongMgr->GetUpgradeData(ID()); - if(data) return data->Rank(s); + if(data) { + return data->Rank(s); + } } - else { - std::map::const_iterator it = mRanks.find(s); - if (it != mRanks.end()) { - return it->second; - } - else return 0; + std::map::const_iterator it = mRanks.find(s); + if (it != mRanks.end()) { + return it->second; } + return 0; } bool BandSongMetadata::HasVocalHarmony() const { @@ -303,9 +303,14 @@ bool BandSongMetadata::HasBass() const { return HasPart(bass, false) || HasPart(real_bass, false); } +// FIXME: how do we get the regular HasKeys() to inline here but not anywhere else? +// this hack is in place for now +inline bool BandSongMetadata::HasKeysDebug() const { + return HasPart(keys, false) || HasPart(real_keys, false); +} + Symbol BandSongMetadata::HasKeysSym() const { - bool haskeypart = HasPart(keys, false) || HasPart(real_keys, false); - return haskeypart ? has_part_yes : has_part_no; + return HasKeysDebug() ? has_part_yes : has_part_no; } bool BandSongMetadata::HasSolo(Symbol s) const { diff --git a/src/band3/meta_band/BandSongMetadata.h b/src/band3/meta_band/BandSongMetadata.h index bc22cdb6..6a1e0908 100644 --- a/src/band3/meta_band/BandSongMetadata.h +++ b/src/band3/meta_band/BandSongMetadata.h @@ -58,6 +58,8 @@ class BandSongMetadata : public SongMetadata { const char* MidiUpdate() const; bool IsDownload() const; + inline bool HasKeysDebug() const; + static int sBandSaveVer; String mTitle; // 0x40 diff --git a/src/band3/meta_band/BandSongMgr.cpp b/src/band3/meta_band/BandSongMgr.cpp index ac1c479a..a8ebee2b 100644 --- a/src/band3/meta_band/BandSongMgr.cpp +++ b/src/band3/meta_band/BandSongMgr.cpp @@ -1,14 +1,17 @@ #include "meta_band/BandSongMgr.h" #include "meta_band/BandSongMetadata.h" #include "obj/Dir.h" +#include "game/GameMode.h" #include "utl/FakeSongMgr.h" #include "utl/Symbols.h" BandSongMgr gSongMgr; BandSongMgr* TheSongMgr = &gSongMgr; +bool BandSongMgr::sFakeSongsAllowed; + BandSongMgr::BandSongMgr() : unkc0(0), unk124(1), mUpgradeMgr(0), mLicenseMgr(0), mMaxSongCount(-1), unk13c(0), unk140(0) { - ClearAndShrink(unk130); + ClearAndShrink(mContentAltDirs); TheBaseSongManger = this; } @@ -16,15 +19,15 @@ void BandSongMgr::Init(){ SongMgr::Init(); SetName("song_mgr", ObjectDir::sMainDir); TheContentMgr->RegisterCallback(this, false); - unkc4.clear(); - unkdc.clear(); + mSongNameLookup.clear(); + mSongIDLookup.clear(); DataArray* cfg = SystemConfig(song_mgr); DataArray* altarr = cfg->FindArray(alt_dirs, false); if(altarr){ for(int i = 1; i < altarr->Size(); i++){ const char* str = altarr->Array(i)->Str(0); if(strlen(str) != 0){ - unk130.push_back(String(str)); + mContentAltDirs.push_back(String(str)); } } } @@ -37,10 +40,10 @@ void BandSongMgr::Terminate(){ RELEASE(unkc0); RELEASE(mLicenseMgr); RELEASE(mUpgradeMgr); - ClearAndShrink(unk130); + ClearAndShrink(mContentAltDirs); TheContentMgr->UnregisterCallback(this, false); - unkc4.clear(); - unkdc.clear(); + mSongNameLookup.clear(); + mSongIDLookup.clear(); } void BandSongMgr::ContentMounted(const char* c1, const char* c2){ @@ -51,10 +54,10 @@ const char* BandSongMgr::ContentDir(){ return "songs"; } Symbol BandSongMgr::GetShortNameFromSongID(int songID, bool fail) const { MILO_ASSERT(songID != kSongID_Invalid && songID != kSongID_Any && songID != kSongID_Random, 0x156); - std::map::const_iterator it = unkc4.find(songID); - if(it != unkc4.end()) return it->second; - it = unkf4.find(songID); - if(it != unkf4.end()) return it->second; + std::map::const_iterator it = mSongNameLookup.find(songID); + if(it != mSongNameLookup.end()) return it->second; + it = mExtraSongIDMap.find(songID); + if(it != mExtraSongIDMap.end()) return it->second; if(fail){ MILO_FAIL("Couldn't find song short name for SONG_ID %d", songID); } @@ -62,13 +65,13 @@ Symbol BandSongMgr::GetShortNameFromSongID(int songID, bool fail) const { } int BandSongMgr::GetSongIDFromShortName(Symbol shortName, bool fail) const { - std::map::const_iterator it = unkdc.find(shortName); - if(it != unkdc.end()){ + std::map::const_iterator it = mSongIDLookup.find(shortName); + if(it != mSongIDLookup.end()){ MILO_ASSERT(it->second != kSongID_Invalid, 0x16D); return it->second; } else { - for(std::map::const_iterator it = unkf4.begin(); it != unkf4.end(); ++it){ + for(std::map::const_iterator it = mExtraSongIDMap.begin(); it != mExtraSongIDMap.end(); ++it){ if(it->second == shortName){ MILO_ASSERT(it->first != kSongID_Invalid, 0x176); return it->first; @@ -161,4 +164,69 @@ int BandSongMgr::NumRankTiers(Symbol s) const { Symbol BandSongMgr::RankTierToken(int i) const { Symbol inst = band; return SystemConfig(song_groupings, rank)->Array(i + 1)->FindSym(inst); -} \ No newline at end of file +} + +void BandSongMgr::GetRankedSongs(std::vector& vec, bool b1, bool b2) const { + if(b1){ + TheGameMode->Property("demos_allowed", true)->Int(0); + } + vec.clear(); + for(std::set::const_iterator it = mAvailableSongs.begin(); it != mAvailableSongs.end(); ++it){ + int cur = *it; + BandSongMetadata* data = (BandSongMetadata*)Data(cur); + if(data->IsRanked() && !data->IsPrivate() && (b2 || !IsRestricted(cur))){ + vec.push_back(*it); + } + } +} + +int BandSongMgr::GetValidSongCount(const std::map& songs) const { + int count = 0; + for(std::map::const_iterator it = songs.begin(); it != songs.end(); ++it){ + BandSongMetadata* data = (BandSongMetadata*)it->second; + if(data && data->IsRanked() && !data->IsPrivate()) count++; + } + return count; +} + +bool BandSongMgr::GetFakeSongsAllowed(){ return sFakeSongsAllowed; } +void BandSongMgr::SetFakeSongsAllowed(bool b){ sFakeSongsAllowed = b; } + +void BandSongMgr::CheatToggleMaxSongCount(){ + DataArray* cfg = SystemConfig(song_mgr); + int max = cfg->FindInt(max_song_count); + int max_debug = cfg->FindInt(max_song_count_debug); + if(mMaxSongCount == max) max = max_debug; + mMaxSongCount = max; +} + +#pragma push +#pragma dont_inline on +BEGIN_HANDLERS(BandSongMgr) + HANDLE_EXPR(has_song, HasSong(_msg->Sym(2), true)) + HANDLE_EXPR(song_path, SongPath(_msg->Sym(2))) + HANDLE_EXPR(song_file_path, SongFilePath(_msg->Sym(2), _msg->Str(3), false)) + HANDLE_EXPR(song_name, SongName(_msg->Sym(2))) + HANDLE_EXPR(upgrade_midi_file, UpgradeMidiFile(GetSongIDFromShortName(_msg->Sym(2), true))) + HANDLE_EXPR(get_meta_data, Data(GetSongIDFromShortName(_msg->Sym(2), true))) + HANDLE_EXPR(rank_tier, RankTier(_msg->Float(2), _msg->Sym(3))) + HANDLE_EXPR(rank_tier_for_song, RankTier(((BandSongMetadata*)Data(GetSongIDFromShortName(_msg->Sym(2), true)))->Rank(_msg->Sym(3)), _msg->Sym(3))) + HANDLE_EXPR(num_rank_tiers, NumRankTiers(_msg->Sym(2))) + HANDLE_EXPR(rank_tier_token, RankTierToken(_msg->Int(2))) + HANDLE_EXPR(num_vocal_parts, GetNumVocalParts(_msg->Sym(2))) + HANDLE_ACTION(add_recent_song, AddRecentSong(GetSongIDFromShortName(_msg->Sym(2), true))) + HANDLE_EXPR(part_plays_in_song, ((BandSongMetadata*)Data(GetSongIDFromShortName(_msg->Sym(2), true)))->HasPart(_msg->Sym(3), false)) + HANDLE_EXPR(mute_win_cues, ((BandSongMetadata*)Data(GetSongIDFromShortName(_msg->Sym(2), true)))->MuteWinCues()) + HANDLE_EXPR(midi_file, MidiFile(_msg->Sym(2))) + HANDLE_EXPR(is_demo, IsDemo(_msg->Int(2))) + HANDLE_EXPR(has_upgrade, GetUpgradeData(GetSongIDFromShortName(_msg->Sym(2), true)) != 0) + HANDLE_EXPR(has_license, HasLicense(_msg->Sym(2))) + HANDLE_EXPR(get_fake_songs_allowed, GetFakeSongsAllowed()) + HANDLE_ACTION(set_fake_songs_allowed, SetFakeSongsAllowed(_msg->Int(2))) + HANDLE_ACTION(sync_shared_songs, SyncSharedSongs()) + HANDLE_EXPR(get_max_song_count, GetMaxSongCount()) + HANDLE_ACTION(cheat_toggle_max_song_count, CheatToggleMaxSongCount()) + HANDLE_SUPERCLASS(SongMgr) + HANDLE_CHECK(0x6DF) +END_HANDLERS +#pragma pop \ No newline at end of file diff --git a/src/band3/meta_band/BandSongMgr.h b/src/band3/meta_band/BandSongMgr.h index 25adedd1..404a236a 100644 --- a/src/band3/meta_band/BandSongMgr.h +++ b/src/band3/meta_band/BandSongMgr.h @@ -44,8 +44,8 @@ class BandSongMgr : public SongMgr { virtual void WriteCachedMetadataFromStream(BinStream&) const; virtual const char* ContentPattern(); virtual const char* ContentDir(); - virtual bool HasContentAltDirs(); - virtual const char* ContentAltDirs(); + virtual bool HasContentAltDirs(){ return !mContentAltDirs.empty(); } + virtual std::vector* ContentAltDirs(){ return &mContentAltDirs; } void AddSongs(DataArray* songs); const char* UpgradeMidiFile(int) const; @@ -57,20 +57,33 @@ class BandSongMgr : public SongMgr { const char* SongPath(Symbol) const; int NumRankTiers(Symbol) const; Symbol RankTierToken(int) const; + void GetRankedSongs(std::vector&, bool, bool) const; + int GetValidSongCount(const std::map&) const; + bool IsRestricted(int) const; + int RankTier(float, Symbol) const; + int GetNumVocalParts(Symbol) const; + void AddRecentSong(int); + bool IsDemo(int) const; + bool HasLicense(Symbol) const; + void SyncSharedSongs(); + int GetMaxSongCount() const; + void CheatToggleMaxSongCount(); static bool GetFakeSongsAllowed(); + static void SetFakeSongsAllowed(bool); + static bool sFakeSongsAllowed; mutable DataArraySongInfo* unkc0; // 0xc0 - std::map unkc4; // 0xc4 - std::map unkdc; // 0xdc - std::map unkf4; // 0xf4 + std::map mSongNameLookup; // 0xc4 + std::map mSongIDLookup; // 0xdc + std::map mExtraSongIDMap; // 0xf4 std::list mSongRankings; // 0x10c std::list unk114; // 0x114 std::vector unk11c; // 0x11c bool unk124; // 0x124 SongUpgradeMgr* mUpgradeMgr; // 0x128 LicenseMgr* mLicenseMgr; // 0x12c - std::vector unk130; + std::vector mContentAltDirs; // 0x130 int mMaxSongCount; // 0x138 bool unk13c; // 0x13c int unk140; // 0x140 diff --git a/src/band3/meta_band/PerformanceData.cpp b/src/band3/meta_band/PerformanceData.cpp index fac247c6..73739cb0 100644 --- a/src/band3/meta_band/PerformanceData.cpp +++ b/src/band3/meta_band/PerformanceData.cpp @@ -93,7 +93,7 @@ void Stats::SetBestOverdriveDeploymentsCount(int bestOverdriveDeploymentsCount) void Stats::AccessBestOverdriveDeployment(int) {} void Stats::SetBestStreakMultipliersCount(int bestStreakMultipliersCount) {} void Stats::AccessBestStreakMultiplier(int) {} -void Stats::SetTotalOverdriveDuration(float totalOverdriveDuration) { mTotalOverdriveDuration = totalOverdriveDuration; } +void Stats::SetTotalOverdriveDuration(float totalOverdriveDuration) { mTotalOverdriveDurationMs = totalOverdriveDuration; } void Stats::SetTotalMultiplierDuration(float totalMultiplierDuration) { mTotalMultiplierDuration = totalMultiplierDuration; } void Stats::SetRollsHitCompletely(int rollsHitCompletely) { mRollsHitCompletely = rollsHitCompletely; } void Stats::SetRollCount(int rollCount) { mRollCount = rollCount; } diff --git a/src/system/beatmatch/SongData.h b/src/system/beatmatch/SongData.h index d496b967..4b0dcdc3 100644 --- a/src/system/beatmatch/SongData.h +++ b/src/system/beatmatch/SongData.h @@ -115,9 +115,12 @@ class SongData : public InternalSongParserSink, public GemListInterface, public unsigned int GetGameCymbalLanes() const; GameGemList* GetGemList(int); + GameGemList* GetGemListByDiff(int, int); AudioTrackNum GetAudioTrackNum(int) const; void Load(SongInfo*, int, PlayerTrackConfigList*, std::vector&, bool, SongDataValidate); bool Poll(); + void SendPhrases(int); + void ChangeTrackDiff(int, int); int unkc; // 0xc int unk10; // 0x10 @@ -157,7 +160,7 @@ class SongData : public InternalSongParserSink, public GemListInterface, public int unke0; // 0xe0 String unke4; // 0xe4 std::map mapf0; // 0xf0 - std::vector mRangeSections; + std::vector mRangeSections; // 0x108 std::vector > mKeyboardRangeSections; int unk118; int unk11c; diff --git a/src/system/beatmatch/SongPos.h b/src/system/beatmatch/SongPos.h index 2e24a0be..390114ae 100644 --- a/src/system/beatmatch/SongPos.h +++ b/src/system/beatmatch/SongPos.h @@ -3,7 +3,7 @@ class SongPos { public: - SongPos(); + SongPos() : mTotalTick(0), mMeasure(0), mBeat(0), mTick(0) {} SongPos(float tt, float tb, int m, int b, int t) : mTotalTick(tt), mTotalBeat(tb), mMeasure(m), mBeat(b), mTick(t) {} float mTotalTick; // 0x0 diff --git a/src/system/os/ContentMgr.h b/src/system/os/ContentMgr.h index 41f324fa..6cf61d83 100644 --- a/src/system/os/ContentMgr.h +++ b/src/system/os/ContentMgr.h @@ -47,7 +47,7 @@ class ContentMgr : public Hmx::Object { virtual void ContentCancelled(){} virtual const char* ContentPattern(){ return ""; } virtual const char* ContentDir(){ return "."; } - virtual const char* ContentAltDirs(){ return 0; } + virtual std::vector* ContentAltDirs(){ return 0; } virtual bool HasContentAltDirs(){ return false; } };