From 99ee1f2b84d7980bcb87e5976944f68bf0755a06 Mon Sep 17 00:00:00 2001
From: teejusb <5017202+teejusb@users.noreply.github.com>
Date: Tue, 21 Dec 2021 23:44:37 -0800
Subject: [PATCH] Use bitset instead of map and adjust files accordingly.
---
Docs/Luadoc/Lua.xml | 4 ++
Docs/Luadoc/LuaDocumentation.xml | 16 +++++++
src/GameConstantsAndTypes.cpp | 1 +
src/Player.cpp | 82 +++++++++++++-------------------
src/PlayerAI.cpp | 20 +++++++-
src/PlayerOptions.cpp | 58 +++++++++++-----------
src/PlayerOptions.h | 11 ++---
7 files changed, 109 insertions(+), 83 deletions(-)
diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml
index b130b3bcce..292e953e92 100644
--- a/Docs/Luadoc/Lua.xml
+++ b/Docs/Luadoc/Lua.xml
@@ -1248,6 +1248,7 @@
+
@@ -1267,6 +1268,7 @@
+
@@ -1319,6 +1321,7 @@
+
@@ -1368,6 +1371,7 @@
+
diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml
index cf1592ffa2..5d8a2c28dc 100644
--- a/Docs/Luadoc/LuaDocumentation.xml
+++ b/Docs/Luadoc/LuaDocumentation.xml
@@ -3915,6 +3915,11 @@ a,b = options:Boost(5)
+
+ Selectively disable specific timing windows for a player.
+ Valid values are W1 to W5 as defined in the enum.
+ Returns a table of TimingWindow with the set of disabled windows after the function call.
+
If the player is using Distant (zero skew and positive tilt), returns the value of tilt and its approach_speed.
Returns nil otherwise.
@@ -3960,6 +3965,9 @@ a,b = options:Boost(5)
+
+ Returns a table of the currently disabled s for the player.
+
Returns true if step attacks or random attacks are enabled.
@@ -4065,6 +4073,9 @@ prev_note_name, succeeded = options:NoteSkin("cel")
+
+ Re-enable all s that may have previously been disabled.
+
Use 1-16 in place of 'n' to apply Reverse on a specific column.
@@ -4165,6 +4176,11 @@ prev_note_name, succeeded = options:NoteSkin("cel")
Returns true if the player is using reverse. (equivalent to GetReverse() == 1.0)
+
+ The time in seconds to adjust a player's visual delay by.
+ Negative values will shift the arrows up, while positive values will push them down.
+ Sub-millisecond visual delay values are not saved and are instead rounded to the closest millisecond.
+
diff --git a/src/GameConstantsAndTypes.cpp b/src/GameConstantsAndTypes.cpp
index 2c6e4d1363..c1ca5653c5 100644
--- a/src/GameConstantsAndTypes.cpp
+++ b/src/GameConstantsAndTypes.cpp
@@ -284,6 +284,7 @@ static const char *TimingWindowNames[] = {
};
XToString( TimingWindow );
LuaXType( TimingWindow );
+StringToX( TimingWindow );
static const char *ScoreEventNames[] = {
"CheckpointHit",
diff --git a/src/Player.cpp b/src/Player.cpp
index e2ed301697..4b968bf483 100644
--- a/src/Player.cpp
+++ b/src/Player.cpp
@@ -104,7 +104,7 @@ static const float StepSearchDistance = 1.0f;
void TimingWindowSecondsInit( size_t /*TimingWindow*/ i, RString &sNameOut, float &defaultValueOut )
{
- sNameOut = "TimingWindowSeconds" + TimingWindowToString( (TimingWindow)i );
+ sNameOut = "TimingWindowSeconds" + TimingWindowToString( static_cast(i) );
switch( i )
{
case TW_W1:
@@ -1003,15 +1003,15 @@ void Player::Update( float fDeltaTime )
{
float largestWindow = 0.0f;
const auto &disabledWindows = m_pPlayerState->m_PlayerOptions.GetCurrent().m_twDisabledWindows;
- if (disabledWindows.find(TW_W1) == disabledWindows.end())
+ if (!disabledWindows[TW_W1])
largestWindow = max(largestWindow, GetWindowSeconds(TW_W1));
- if (disabledWindows.find(TW_W2) == disabledWindows.end())
+ if (!disabledWindows[TW_W2])
largestWindow = max(largestWindow, GetWindowSeconds(TW_W2));
- if (disabledWindows.find(TW_W3) == disabledWindows.end())
+ if (!disabledWindows[TW_W3])
largestWindow = max(largestWindow, GetWindowSeconds(TW_W3));
- if (disabledWindows.find(TW_W4) == disabledWindows.end())
+ if (!disabledWindows[TW_W4])
largestWindow = max(largestWindow, GetWindowSeconds(TW_W4));
- if (disabledWindows.find(TW_W5) == disabledWindows.end())
+ if (!disabledWindows[TW_W5])
largestWindow = max(largestWindow, GetWindowSeconds(TW_W5));
// We have to check the unjudged notes that are within the
@@ -2272,15 +2272,15 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele
{
// Set it to the first non-disabled window.
const auto &disabledWindows = m_pPlayerState->m_PlayerOptions.GetCurrent().m_twDisabledWindows;
- if (disabledWindows.find(TW_W1) == disabledWindows.end())
+ if (!disabledWindows[TW_W1])
score = TNS_W1;
- else if (disabledWindows.find(TW_W2) == disabledWindows.end())
+ else if (!disabledWindows[TW_W2])
score = TNS_W2;
- else if (disabledWindows.find(TW_W3) == disabledWindows.end())
+ else if (!disabledWindows[TW_W3])
score = TNS_W3;
- else if (disabledWindows.find(TW_W4) == disabledWindows.end())
+ else if (!disabledWindows[TW_W4])
score = TNS_W4;
- else if (disabledWindows.find(TW_W5) == disabledWindows.end())
+ else if (!disabledWindows[TW_W5])
score = TNS_W5;
break;
@@ -2290,11 +2290,11 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele
if( (pTN->type == TapNoteType_Lift) == bRelease )
{
const auto &disabledWindows = m_pPlayerState->m_PlayerOptions.GetCurrent().m_twDisabledWindows;
- if( fSecondsFromExact <= GetWindowSeconds(TW_W1) && disabledWindows.find(TW_W1) == disabledWindows.end()) score = TNS_W1;
- else if( fSecondsFromExact <= GetWindowSeconds(TW_W2) && disabledWindows.find(TW_W2) == disabledWindows.end()) score = TNS_W2;
- else if( fSecondsFromExact <= GetWindowSeconds(TW_W3) && disabledWindows.find(TW_W3) == disabledWindows.end()) score = TNS_W3;
- else if( fSecondsFromExact <= GetWindowSeconds(TW_W4) && disabledWindows.find(TW_W4) == disabledWindows.end()) score = TNS_W4;
- else if( fSecondsFromExact <= GetWindowSeconds(TW_W5) && disabledWindows.find(TW_W5) == disabledWindows.end()) score = TNS_W5;
+ if( fSecondsFromExact <= GetWindowSeconds(TW_W1) && !disabledWindows[TW_W1] ) score = TNS_W1;
+ else if( fSecondsFromExact <= GetWindowSeconds(TW_W2) && !disabledWindows[TW_W2] ) score = TNS_W2;
+ else if( fSecondsFromExact <= GetWindowSeconds(TW_W3) && !disabledWindows[TW_W3] ) score = TNS_W3;
+ else if( fSecondsFromExact <= GetWindowSeconds(TW_W4) && !disabledWindows[TW_W4] ) score = TNS_W4;
+ else if( fSecondsFromExact <= GetWindowSeconds(TW_W5) && !disabledWindows[TW_W5] ) score = TNS_W5;
}
break;
}
@@ -2304,30 +2304,14 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele
case PC_AUTOPLAY:
{
score = PlayerAI::GetTapNoteScore(m_pPlayerState);
- const auto& disabledWindows = m_pPlayerState->m_PlayerOptions.GetCurrent().m_twDisabledWindows;
- for (int i = score; i >= TNS_W5; i--)
- {
- TapNoteScore cur_score = (TapNoteScore)i;
- // Downgrade the TapNoteScore if that specific window is disabled.
- if (cur_score == TNS_W1 && disabledWindows.find(TW_W1) == disabledWindows.end())
- score = TNS_W2;
- else if (cur_score == TNS_W2 && disabledWindows.find(TW_W2) == disabledWindows.end())
- score = TNS_W3;
- else if (cur_score == TNS_W3 && disabledWindows.find(TW_W3) == disabledWindows.end())
- score = TNS_W4;
- else if (cur_score == TNS_W4 && disabledWindows.find(TW_W4) == disabledWindows.end())
- score = TNS_W5;
- else if (cur_score == TNS_W5 && disabledWindows.find(TW_W5) == disabledWindows.end())
- score = TNS_None;
- }
/* XXX: This doesn't make sense.
* Step should only be called in autoplay for hit notes. */
#if 0
- // GetTapNoteScore always returns TNS_W1 in autoplay.
- // If the step is far away, don't judge it.
- if (m_pPlayerState->m_PlayerController == PC_AUTOPLAY &&
- fSecondsFromExact > GetWindowSeconds(TW_W5))
+ // GetTapNoteScore always returns TNS_W1 in autoplay.
+ // If the step is far away, don't judge it.
+ if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY &&
+ fSecondsFromExact > GetWindowSeconds(TW_W5) )
{
score = TNS_None;
break;
@@ -2336,36 +2320,36 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele
// TRICKY: We're asking the AI to judge mines. Consider TNS_W4 and
// below as "mine was hit" and everything else as "mine was avoided"
- if (pTN->type == TapNoteType_Mine)
+ if ( pTN->type == TapNoteType_Mine )
{
// The CPU hits a lot of mines. Only consider hitting the
// first mine for a row. We know we're the first mine if
// there are are no mines to the left of us.
- for (int t = 0; t < col; t++)
+ for ( int t=0; t= get_to_avoid)
+ if (score >= get_to_avoid )
return; // avoided
else
score = TNS_HitMine;
}
- if (pTN->type == TapNoteType_Attack && score > TNS_W4)
+ if ( pTN->type == TapNoteType_Attack && score > TNS_W4 )
score = TNS_W2; // sentinel
/* AI will generate misses here. Don't handle a miss like a regular
* note because we want the judgment animation to appear delayed.
* Instead, return early if AI generated a miss, and let
* UpdateTapNotesMissedOlderThan() detect and handle the misses. */
- if (score == TNS_Miss)
+ if ( score == TNS_Miss )
return;
// Put some small, random amount in fNoteOffset so that demonstration
@@ -2383,7 +2367,7 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele
float fWindowW5 = GetWindowSeconds(TW_W5);
// W1 is the top judgment, there is no overlap.
- if (score == TNS_W1)
+ if ( score == TNS_W1 )
fNoteOffset = randomf(-fWindowW1, fWindowW1);
else
{
@@ -2391,25 +2375,25 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele
float fLowerBound = 0.0f; // negative upper limit
float fUpperBound = 0.0f; // positive lower limit
float fCompareWindow = 0.0f; // filled in here:
- if (score == TNS_W2)
+ if ( score == TNS_W2 )
{
fLowerBound = -fWindowW1;
fUpperBound = fWindowW1;
fCompareWindow = fWindowW2;
}
- else if (score == TNS_W3)
+ else if ( score == TNS_W3 )
{
fLowerBound = -fWindowW2;
fUpperBound = fWindowW2;
fCompareWindow = fWindowW3;
}
- else if (score == TNS_W4)
+ else if ( score == TNS_W4 )
{
fLowerBound = -fWindowW3;
fUpperBound = fWindowW3;
fCompareWindow = fWindowW4;
}
- else if (score == TNS_W5)
+ else if ( score == TNS_W5 )
{
fLowerBound = -fWindowW4;
fUpperBound = fWindowW4;
@@ -2418,7 +2402,7 @@ void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRele
float f1 = randomf(-fCompareWindow, fLowerBound);
float f2 = randomf(fUpperBound, fCompareWindow);
- if (randomf() * 100 >= 50)
+ if(randomf() * 100 >= 50)
fNoteOffset = f1;
else
fNoteOffset = f2;
diff --git a/src/PlayerAI.cpp b/src/PlayerAI.cpp
index 44cf43e706..418f40147b 100644
--- a/src/PlayerAI.cpp
+++ b/src/PlayerAI.cpp
@@ -141,7 +141,25 @@ TapNoteScore PlayerAI::GetTapNoteScore( const PlayerState* pPlayerState )
TapScoreDistribution& distribution = g_Distributions[iCpuSkill];
- return distribution.GetTapNoteScore();
+ TapNoteScore score = distribution.GetTapNoteScore();
+
+ const auto& disabledWindows = pPlayerState->m_PlayerOptions.GetCurrent().m_twDisabledWindows;
+ for (int i = score; i >= TNS_W5; i--)
+ {
+ TapNoteScore cur_score = (TapNoteScore)i;
+ // Downgrade the TapNoteScore if that specific window is disabled.
+ if (cur_score == TNS_W1 && disabledWindows[TW_W1])
+ score = TNS_W2;
+ if (cur_score == TNS_W2 && disabledWindows[TW_W2])
+ score = TNS_W3;
+ if (cur_score == TNS_W3 && disabledWindows[TW_W3])
+ score = TNS_W4;
+ if (cur_score == TNS_W4 && disabledWindows[TW_W4])
+ score = TNS_W5;
+ if (cur_score == TNS_W5 && disabledWindows[TW_W5])
+ score = TNS_None;
+ }
+ return score;
}
/*
diff --git a/src/PlayerOptions.cpp b/src/PlayerOptions.cpp
index 72c26b3e4c..e268d60203 100644
--- a/src/PlayerOptions.cpp
+++ b/src/PlayerOptions.cpp
@@ -97,7 +97,7 @@ void PlayerOptions::Init()
m_bCosecant = false;
m_sNoteSkin = "";
m_fVisualDelay = 0.0f;
- m_twDisabledWindows.clear();
+ m_twDisabledWindows.reset();
ZERO( m_fMovesX ); ONE( m_SpeedfMovesX );
ZERO( m_fMovesY ); ONE( m_SpeedfMovesY );
ZERO( m_fMovesZ ); ONE( m_SpeedfMovesZ );
@@ -568,14 +568,19 @@ void PlayerOptions::GetMods( vector &AddTo, bool bForceNoteSkin ) const
AddTo.push_back( ssprintf("%.0fms VisualDelay", m_fVisualDelay * 1000.0f) );
}
- if (!m_twDisabledWindows.empty()) {
+ if (m_twDisabledWindows.count() != 0) {
std::stringstream ss;
+ bool is_first = true;
ss << "No ";
- for (auto it=m_twDisabledWindows.begin(); it != m_twDisabledWindows.end(); ++it) {
- if (it != m_twDisabledWindows.begin()) {
- ss << "/";
+ for (int i=TW_W1; i != TW_W5; ++i) {
+ if (m_twDisabledWindows[i]) {
+ if (!is_first) {
+ ss << "/";
+ } else {
+ is_first = true;
+ }
+ ss << TimingWindowToString(static_cast(i)).c_str();
}
- ss << TimingWindowToString(*it).c_str();
}
// Final string will be something like "No W4/W5"
@@ -1162,13 +1167,6 @@ bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut
else if( sBit == "visualdelay" ) m_fVisualDelay = level;
else if( level == 0 && disabledWindows.Compare(sBit)) // "No w1" etc.
{
- static std::map nameToWindow = {
- {"w1", TW_W1},
- {"w2", TW_W2},
- {"w3", TW_W3},
- {"w4", TW_W4},
- {"w5", TW_W5},
- };
// We come into this condition if there is at least a single window present but there may be more.
// To get all of the windows, we go through in a loop to extract all of them.
static Regex allDisabledWindows("(w[1-5])(.*)$");
@@ -1178,9 +1176,11 @@ bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut
if (!allDisabledWindows.Compare(input, matches))
break;
- if (nameToWindow.find(matches[0]) != nameToWindow.end())
+ TimingWindow tw;
+ bool ret = StringConversion::FromString(matches[0].MakeUpper(), tw);
+ if (ret && TW_W1 <= tw && tw <= TW_W5)
{
- m_twDisabledWindows.insert(nameToWindow[matches[0]]);
+ m_twDisabledWindows.set(tw);
}
input = matches[1];
}
@@ -2044,13 +2044,17 @@ public:
if (original_top >= 1 && !lua_isnil(L, 1))
{
// Insert the specified TimingWindow into the disabled windows set.
- p->m_twDisabledWindows.insert(Enum::Check(L, 1));
+ p->m_twDisabledWindows.set(Enum::Check(L, 1));
}
// Construct a new table indicating all of the disabled windows.
lua_newtable( L );
- for (TimingWindow window : p->m_twDisabledWindows)
- {
- Enum::Push(L, window);
+ int j = 0;
+ for (int i=TW_W1; i != TW_W5; ++i) {
+ if (p->m_twDisabledWindows[i]) {
+ Enum::Push(L, static_cast(i));
+ lua_rawseti( L, -2, j+1 );
+ ++j;
+ }
}
OPTIONAL_RETURN_SELF(original_top);
return 1;
@@ -2059,7 +2063,7 @@ public:
static int ResetDisabledTimingWindows(T* p, lua_State* L)
{
int original_top= lua_gettop(L);
- p->m_twDisabledWindows.clear();
+ p->m_twDisabledWindows.reset();
OPTIONAL_RETURN_SELF(original_top);
return 1;
}
@@ -2068,14 +2072,14 @@ public:
{
int original_top= lua_gettop(L);
lua_newtable( L );
- int i = 0;
- for (TimingWindow window : p->m_twDisabledWindows)
- {
- Enum::Push(L, window);
- lua_rawseti( L, -2, i+1 );
- ++i;
+ int j = 0;
+ for (int i=TW_W1; i != TW_W5; ++i) {
+ if (p->m_twDisabledWindows[i]) {
+ Enum::Push(L, static_cast(i));
+ lua_rawseti( L, -2, j+1 );
+ ++j;
+ }
}
- OPTIONAL_RETURN_SELF(original_top);
return 1;
}
diff --git a/src/PlayerOptions.h b/src/PlayerOptions.h
index 3ff8d3ec52..6a0dfd9ad5 100644
--- a/src/PlayerOptions.h
+++ b/src/PlayerOptions.h
@@ -9,7 +9,7 @@ struct lua_State;
#define ONE( arr ) { for( unsigned Z = 0; Z < ARRAYLEN(arr); ++Z ) arr[Z]=1.0f; }
-#include
+#include
#include "GameConstantsAndTypes.h"
#include "PlayerNumber.h"
@@ -400,11 +400,10 @@ public:
/** @brief The Visual Delay additionally applied on a per-player basis in ms. */
float m_fVisualDelay;
- /** @brief The TimingWindow that can be disabled. Valid values are only W1-W5.
- * Other values are ignore.
- * We use a set instead of unordered_set because we want the ordering so that
- * the generated player options string is consistent. */
- std::set m_twDisabledWindows;
+ /** @brief The TimingWindow that can be disabled.
+ * Valid values are only W1-W5 which map to indices 0-5 respectively.
+ * Other values are ignored. */
+ std::bitset<5> m_twDisabledWindows;
void NextAccel();
void NextEffect();