Files
itgmania212121/src/ArrowEffects.cpp
T

1686 lines
64 KiB
C++
Raw Normal View History

2011-03-17 01:47:30 -04:00
#include "global.h"
#include "ArrowEffects.h"
#include "Steps.h"
#include "GameConstantsAndTypes.h"
#include "GameManager.h"
#include "RageTimer.h"
#include "NoteDisplay.h"
#include "Song.h"
#include "RageMath.h"
#include "ScreenDimensions.h"
#include "PlayerState.h"
#include "GameState.h"
#include "Style.h"
#include "ThemeMetric.h"
#include <float.h>
static char const dimension_names[4]= "XYZ";
2011-03-17 01:47:30 -04:00
static ThemeMetric<float> ARROW_SPACING( "ArrowEffects", "ArrowSpacing" );
static ThemeMetric<bool> QUANTIZE_ARROW_Y( "ArrowEffects", "QuantizeArrowYPosition");
/* For better or for worse, allow the themes to modify the various mod
* effects for the different mods. In general, it is recommended to not
* edit the default values and instead use percentage mods when changes
* are wanted. Still, the option is available for those that want it.
*
* Is this a good idea? We'll find out. -aj & Wolfman2000 */
static ThemeMetric<float> BLINK_MOD_FREQUENCY( "ArrowEffects", "BlinkModFrequency" );
static ThemeMetric<float> BOOST_MOD_MIN_CLAMP( "ArrowEffects", "BoostModMinClamp" );
static ThemeMetric<float> BOOST_MOD_MAX_CLAMP( "ArrowEffects", "BoostModMaxClamp" );
static ThemeMetric<float> BRAKE_MOD_MIN_CLAMP( "ArrowEffects", "BrakeModMinClamp" );
static ThemeMetric<float> BRAKE_MOD_MAX_CLAMP( "ArrowEffects", "BrakeModMaxClamp" );
static ThemeMetric<float> WAVE_MOD_MAGNITUDE( "ArrowEffects", "WaveModMagnitude" );
static ThemeMetric<float> WAVE_MOD_HEIGHT( "ArrowEffects", "WaveModHeight" );
static ThemeMetric<float> BOOMERANG_PEAK_PERCENTAGE( "ArrowEffects", "BoomerangPeakPercentage" );
static ThemeMetric<float> EXPAND_MULTIPLIER_FREQUENCY( "ArrowEffects", "ExpandMultiplierFrequency" );
static ThemeMetric<float> EXPAND_MULTIPLIER_SCALE_FROM_LOW( "ArrowEffects", "ExpandMultiplierScaleFromLow" );
static ThemeMetric<float> EXPAND_MULTIPLIER_SCALE_FROM_HIGH( "ArrowEffects", "ExpandMultiplierScaleFromHigh" );
static ThemeMetric<float> EXPAND_MULTIPLIER_SCALE_TO_LOW( "ArrowEffects", "ExpandMultiplierScaleToLow" );
static ThemeMetric<float> EXPAND_MULTIPLIER_SCALE_TO_HIGH( "ArrowEffects", "ExpandMultiplierScaleToHigh" );
static ThemeMetric<float> EXPAND_SPEED_SCALE_FROM_LOW( "ArrowEffects", "ExpandSpeedScaleFromLow" );
static ThemeMetric<float> EXPAND_SPEED_SCALE_FROM_HIGH( "ArrowEffects", "ExpandSpeedScaleFromHigh" );
static ThemeMetric<float> EXPAND_SPEED_SCALE_TO_LOW( "ArrowEffects", "ExpandSpeedScaleToLow" );
static ThemeMetric<float> TIPSY_TIMER_FREQUENCY( "ArrowEffects", "TipsyTimerFrequency" );
static ThemeMetric<float> TIPSY_COLUMN_FREQUENCY( "ArrowEffects", "TipsyColumnFrequency" );
static ThemeMetric<float> TIPSY_ARROW_MAGNITUDE( "ArrowEffects", "TipsyArrowMagnitude" );
static ThemeMetric<float> TIPSY_OFFSET_TIMER_FREQUENCY( "ArrowEffects", "TipsyOffsetTimerFrequency" );
static ThemeMetric<float> TIPSY_OFFSET_COLUMN_FREQUENCY( "ArrowEffects", "TipsyOffsetColumnFrequency" );
static ThemeMetric<float> TIPSY_OFFSET_ARROW_MAGNITUDE( "ArrowEffects", "TipsyOffsetArrowMagnitude" );
static RString TPSTL_NAME(size_t i) { return ssprintf("Tornado%cPositionScaleToLow", dimension_names[i]); }
static ThemeMetric1D<float> TORNADO_POSITION_SCALE_TO_LOW("ArrowEffects", TPSTL_NAME, 3);
static RString TPSTH_NAME(size_t i) { return ssprintf("Tornado%cPositionScaleToHigh", dimension_names[i]); }
static ThemeMetric1D<float> TORNADO_POSITION_SCALE_TO_HIGH("ArrowEffects", TPSTH_NAME, 3);
static RString TOF_NAME(size_t i) { return ssprintf("Tornado%cOffsetFrequency", dimension_names[i]); }
static ThemeMetric1D<float> TORNADO_OFFSET_FREQUENCY("ArrowEffects", TOF_NAME, 3);
static RString TOSFL_NAME(size_t i) { return ssprintf("Tornado%cOffsetScaleFromLow", dimension_names[i]); }
static ThemeMetric1D<float> TORNADO_OFFSET_SCALE_FROM_LOW("ArrowEffects", TOSFL_NAME, 3);
static RString TOSFH_NAME(size_t i) { return ssprintf("Tornado%cOffsetScaleFromHigh", dimension_names[i]); }
static ThemeMetric1D<float> TORNADO_OFFSET_SCALE_FROM_HIGH("ArrowEffects", TOSFH_NAME, 3);
2011-03-17 01:47:30 -04:00
static ThemeMetric<float> DRUNK_COLUMN_FREQUENCY( "ArrowEffects", "DrunkColumnFrequency" );
static ThemeMetric<float> DRUNK_OFFSET_FREQUENCY( "ArrowEffects", "DrunkOffsetFrequency" );
static ThemeMetric<float> DRUNK_ARROW_MAGNITUDE( "ArrowEffects", "DrunkArrowMagnitude" );
static ThemeMetric<float> DRUNK_Z_COLUMN_FREQUENCY( "ArrowEffects", "DrunkZColumnFrequency" );
static ThemeMetric<float> DRUNK_Z_OFFSET_FREQUENCY( "ArrowEffects", "DrunkZOffsetFrequency" );
static ThemeMetric<float> DRUNK_Z_ARROW_MAGNITUDE( "ArrowEffects", "DrunkZArrowMagnitude" );
2011-03-17 01:47:30 -04:00
static ThemeMetric<float> BEAT_OFFSET_HEIGHT( "ArrowEffects", "BeatOffsetHeight" );
static ThemeMetric<float> BEAT_PI_HEIGHT( "ArrowEffects", "BeatPIHeight" );
static ThemeMetric<float> BEAT_Y_OFFSET_HEIGHT( "ArrowEffects", "BeatYOffsetHeight" );
static ThemeMetric<float> BEAT_Y_PI_HEIGHT( "ArrowEffects", "BeatYPIHeight" );
static ThemeMetric<float> BEAT_Z_OFFSET_HEIGHT( "ArrowEffects", "BeatZOffsetHeight" );
static ThemeMetric<float> BEAT_Z_PI_HEIGHT( "ArrowEffects", "BeatZPIHeight" );
2011-07-05 16:50:06 -04:00
static ThemeMetric<float> TINY_PERCENT_BASE( "ArrowEffects", "TinyPercentBase" );
static ThemeMetric<float> TINY_PERCENT_GATE( "ArrowEffects", "TinyPercentGate" );
2011-03-17 01:47:30 -04:00
2019-06-22 12:35:38 -07:00
static const PlayerOptions* curr_options= nullptr;
2011-06-12 03:37:10 -04:00
float ArrowGetPercentVisible(float fYPosWithoutReverse, int iCol, float fYOffset);
static float GetNoteFieldHeight()
2011-03-17 01:47:30 -04:00
{
return SCREEN_HEIGHT + fabsf(curr_options->m_fPerspectiveTilt)*200;
2011-03-17 01:47:30 -04:00
}
float ArrowEffects::GetTime()
{
float mult = 1.f + curr_options->m_fModTimerMult;
float offset = curr_options->m_fModTimerOffset;
ModTimerType modtimer = curr_options->m_ModTimerType;
switch(modtimer)
{
case ModTimerType_Default:
case ModTimerType_Game:
return (RageTimer::GetTimeSinceStartFast()+offset)*mult;
case ModTimerType_Beat:
return (GAMESTATE->m_Position.m_fSongBeatVisible+offset)*mult;
case ModTimerType_Song:
return (GAMESTATE->m_Position.m_fMusicSeconds+offset)*mult;
default:
return RageTimer::GetTimeSinceStartFast()+offset;
}
}
2011-03-17 01:47:30 -04:00
namespace
{
struct PerPlayerData
{
float m_MinTornado[3][MAX_COLS_PER_PLAYER];
float m_MaxTornado[3][MAX_COLS_PER_PLAYER];
2011-03-17 01:47:30 -04:00
float m_fInvertDistance[MAX_COLS_PER_PLAYER];
float m_tipsy_result[MAX_COLS_PER_PLAYER];
float m_tipsy_offset_result[MAX_COLS_PER_PLAYER];
2017-07-14 22:32:58 -07:00
float m_tan_tipsy_result[MAX_COLS_PER_PLAYER];
float m_tan_tipsy_offset_result[MAX_COLS_PER_PLAYER];
float m_fBeatFactor[3];
float m_fExpandSeconds;
2017-07-14 22:32:58 -07:00
float m_fTanExpandSeconds;
// m_prev_style is for checking whether ArrowEffects::Init needs to be
// called. Finding all the placed ArrowEffects is used and making sure
// they all call Init after changing style is non-trivial and more likely
// to cause bugs. -Kyz
Style const* m_prev_style;
2011-03-17 01:47:30 -04:00
};
PerPlayerData g_EffectData[NUM_PLAYERS];
int const dim_x= 0;
int const dim_y= 1;
int const dim_z= 2;
float tornado_position_scale_to_low[3];
float tornado_position_scale_to_high[3];
float tornado_offset_frequency[3];
float tornado_offset_scale_from_low[3];
float tornado_offset_scale_from_high[3];
2011-03-17 01:47:30 -04:00
};
2017-07-14 22:32:58 -07:00
static float SelectTanType(float angle, bool is_cosec)
{
if (is_cosec)
return RageFastCsc(angle);
else
return RageFastTan(angle);
}
static float CalculateTornadoOffsetFromMagnitude(int dimension, int col_id,
float magnitude, float effect_offset, float period,
const Style::ColumnInfo* pCols, float field_zoom,
2017-07-14 22:32:58 -07:00
PerPlayerData& data, float y_offset, bool is_tan)
{
float const real_pixel_offset= pCols[col_id].fXOffset * field_zoom;
float const position_between= SCALE(real_pixel_offset,
data.m_MinTornado[dimension][col_id] * field_zoom,
data.m_MaxTornado[dimension][col_id] * field_zoom,
tornado_position_scale_to_low[dimension],
tornado_position_scale_to_high[dimension]);
float rads= acosf(position_between);
float frequency= tornado_offset_frequency[dimension];
rads+= (y_offset + effect_offset) * ((period * frequency) + frequency) / SCREEN_HEIGHT;
2017-07-14 22:32:58 -07:00
float processed_rads = is_tan ? SelectTanType(rads, curr_options->m_bCosecant) : RageFastCos(rads);
float const adjusted_pixel_offset= SCALE(processed_rads,
tornado_offset_scale_from_low[dimension],
tornado_offset_scale_from_high[dimension],
data.m_MinTornado[dimension][col_id] * field_zoom,
data.m_MaxTornado[dimension][col_id] * field_zoom);
return (adjusted_pixel_offset - real_pixel_offset) * magnitude;
}
2017-07-14 22:32:58 -07:00
static float CalculateDrunkAngle(float speed, int col, float offset,
float col_frequency, float y_offset, float period, float offset_frequency)
{
float time = ArrowEffects::GetTime();
return time * (1+speed) + col*( (offset*col_frequency) + col_frequency)
+ y_offset * ( (period*offset_frequency) + offset_frequency) / SCREEN_HEIGHT;
}
static float CalculateBumpyAngle(float y_offset, float offset, float period)
{
return (y_offset+(100.0f*offset))/((period*16.0f)+16.0f);
}
static float CalculateDigitalAngle(float y_offset, float offset, float period)
{
return PI * (y_offset + (1.0f * offset ) ) / (ARROW_SIZE + (period * ARROW_SIZE) );
}
static void UpdateBeat(int dimension, PerPlayerData &data, const SongPosition &position, float beat_offset, float beat_mult)
{
float fAccelTime = 0.2f, fTotalTime = 0.5f;
float fBeat = ((position.m_fSongBeatVisible + fAccelTime + beat_offset) * (beat_mult+1));
const bool bEvenBeat = ( int(fBeat) % 2 ) != 0;
data.m_fBeatFactor[dimension] = 0;
if( fBeat < 0 )
return;
// -100.2 -> -0.2 -> 0.2
fBeat -= truncf( fBeat );
fBeat += 1;
fBeat -= truncf( fBeat );
if( fBeat >= fTotalTime )
return;
if( fBeat < fAccelTime )
{
data.m_fBeatFactor[dimension] = SCALE( fBeat, 0.0f, fAccelTime, 0.0f, 1.0f);
data.m_fBeatFactor[dimension] *= data.m_fBeatFactor[dimension];
} else /* fBeat < fTotalTime */ {
data.m_fBeatFactor[dimension] = SCALE( fBeat, fAccelTime, fTotalTime, 1.0f, 0.0f);
data.m_fBeatFactor[dimension] = 1 - (1-data.m_fBeatFactor[dimension]) * (1-data.m_fBeatFactor[dimension]);
}
if( bEvenBeat )
data.m_fBeatFactor[dimension] *= -1;
data.m_fBeatFactor[dimension] *= 20.0f;
}
2017-07-14 22:32:58 -07:00
static void UpdateTipsy(float * tipsy_result, float * tipsy_offset_result, float offset, float speed, bool is_tan)
{
const float time= ArrowEffects::GetTime();
const float time_times_timer= time * ((speed * TIPSY_TIMER_FREQUENCY) + TIPSY_TIMER_FREQUENCY);
const float arrow_times_mag= ARROW_SIZE * TIPSY_ARROW_MAGNITUDE;
const float time_times_offset_timer= time *
TIPSY_OFFSET_TIMER_FREQUENCY;
const float arrow_times_offset_mag= ARROW_SIZE *
TIPSY_OFFSET_ARROW_MAGNITUDE;
for(int col= 0; col < MAX_COLS_PER_PLAYER; ++col)
{
if (is_tan)
{
tipsy_result[col]= SelectTanType(time_times_timer + (col * ((offset *
TIPSY_COLUMN_FREQUENCY) + TIPSY_COLUMN_FREQUENCY)), curr_options->m_bCosecant)
* arrow_times_mag;
tipsy_offset_result[col]= SelectTanType(time_times_offset_timer + (col *
TIPSY_OFFSET_COLUMN_FREQUENCY), curr_options->m_bCosecant)
* arrow_times_offset_mag;
}
else
{
tipsy_result[col]= RageFastCos(time_times_timer + (col * ((offset *
TIPSY_COLUMN_FREQUENCY) + TIPSY_COLUMN_FREQUENCY))) * arrow_times_mag;
tipsy_offset_result[col]= RageFastCos(time_times_offset_timer + (col *
TIPSY_OFFSET_COLUMN_FREQUENCY)) * arrow_times_offset_mag;
}
}
}
void ArrowEffects::Init(PlayerNumber pn)
{
const Style* pStyle = GAMESTATE->GetCurrentStyle(pn);
const Style::ColumnInfo* pCols = pStyle->m_ColumnInfo[pn];
PerPlayerData &data = g_EffectData[pn];
// Init tornado limits.
// This used to run every frame, but it doesn't actually depend on anything
// that changes every frame. In openitg, it runs for every note. -Kyz
// TRICKY: Tornado is very unplayable in doubles, so use a smaller
// tornado width if there are many columns
/* the wide_field check makes an assumption for dance mode.
* perhaps check if we are actually playing on singles without,
* say more than 6 columns. That would exclude IIDX, pop'n, and
* techno-8, all of which would be very hectic.
* certain non-singles modes (like halfdoubles 6cols)
* could possibly have tornado enabled.
* let's also take default resolution (640x480) into mind. -aj */
bool wide_field= pStyle->m_iColsPerPlayer > 4;
int max_player_col= pStyle->m_iColsPerPlayer-1;
for(int dimension= 0; dimension < 3; ++dimension)
{
int width= 3;
// wide_field only matters for x, which is dimension 0. -Kyz
if(dimension == 0 && wide_field)
{
width= 2;
}
for(int col_id= 0; col_id <= max_player_col; ++col_id)
{
int start_col= col_id - width;
int end_col= col_id + width;
CLAMP(start_col, 0, max_player_col);
CLAMP(end_col, 0, max_player_col);
data.m_MinTornado[dimension][col_id]= FLT_MAX;
data.m_MaxTornado[dimension][col_id]= FLT_MIN;
for(int i= start_col; i <= end_col; ++i)
{
// Using the x offset when the dimension might be y or z feels so
// wrong, but it provides min and max values when otherwise the
// limits would just be zero, which would make it do nothing. -Kyz
data.m_MinTornado[dimension][col_id] = min(pCols[i].fXOffset, data.m_MinTornado[dimension][col_id]);
data.m_MaxTornado[dimension][col_id] = max(pCols[i].fXOffset, data.m_MaxTornado[dimension][col_id]);
}
}
tornado_position_scale_to_low[dimension]= TORNADO_POSITION_SCALE_TO_LOW.GetValue(dimension);
tornado_position_scale_to_high[dimension]= TORNADO_POSITION_SCALE_TO_HIGH.GetValue(dimension);
tornado_offset_frequency[dimension]= TORNADO_OFFSET_FREQUENCY.GetValue(dimension);
tornado_offset_scale_from_low[dimension]= TORNADO_OFFSET_SCALE_FROM_LOW.GetValue(dimension);
tornado_offset_scale_from_high[dimension]= TORNADO_OFFSET_SCALE_FROM_HIGH.GetValue(dimension);
}
}
2011-03-17 01:47:30 -04:00
void ArrowEffects::Update()
{
static float fLastTime = 0;
float fTime = RageTimer::GetTimeSinceStartFast();
2011-11-01 15:25:05 -04:00
FOREACH_EnabledPlayer( pn )
2011-03-17 01:47:30 -04:00
{
const Style* pStyle = GAMESTATE->GetCurrentStyle(pn);
2011-03-17 01:47:30 -04:00
const Style::ColumnInfo* pCols = pStyle->m_ColumnInfo[pn];
2011-05-17 14:16:58 -04:00
const SongPosition &position = GAMESTATE->m_bIsUsingStepTiming
2011-05-13 23:32:14 -04:00
? GAMESTATE->m_pPlayerState[pn]->m_Position : GAMESTATE->m_Position;
const float field_zoom= GAMESTATE->m_pPlayerState[pn]->m_NotefieldZoom;
const float* effects= GAMESTATE->m_pPlayerState[pn]->m_PlayerOptions.GetCurrent().m_fEffects;
2017-03-14 03:27:18 -07:00
const float* accels= GAMESTATE->m_pPlayerState[pn]->m_PlayerOptions.GetCurrent().m_fAccels;
2011-03-17 01:47:30 -04:00
PerPlayerData &data = g_EffectData[pn];
if(pStyle != data.m_prev_style)
{
Init(pn);
data.m_prev_style= pStyle;
}
if( !position.m_bFreeze || !position.m_bDelay )
2011-03-17 01:47:30 -04:00
{
data.m_fExpandSeconds += fTime - fLastTime;
data.m_fExpandSeconds = fmodf( data.m_fExpandSeconds, (PI*2)/(accels[PlayerOptions::ACCEL_EXPAND_PERIOD]+1) );
2017-07-14 22:32:58 -07:00
data.m_fTanExpandSeconds += fTime - fLastTime;
data.m_fTanExpandSeconds = fmodf( data.m_fTanExpandSeconds, (PI*2)/(accels[PlayerOptions::ACCEL_TAN_EXPAND_PERIOD]+1) );
2011-03-17 01:47:30 -04:00
}
// Update Invert
for( int iColNum = 0; iColNum < MAX_COLS_PER_PLAYER; ++iColNum )
{
const int iNumCols = pStyle->m_iColsPerPlayer;
const int iNumSides = (pStyle->m_StyleType==StyleType_OnePlayerTwoSides ||
pStyle->m_StyleType==StyleType_TwoPlayersSharedSides) ? 2 : 1;
const int iNumColsPerSide = iNumCols / iNumSides;
const int iSideIndex = iColNum / iNumColsPerSide;
const int iColOnSide = iColNum % iNumColsPerSide;
const int iColLeftOfMiddle = (iNumColsPerSide-1)/2;
const int iColRightOfMiddle = (iNumColsPerSide+1)/2;
int iFirstColOnSide = -1;
int iLastColOnSide = -1;
if( iColOnSide <= iColLeftOfMiddle )
{
iFirstColOnSide = 0;
iLastColOnSide = iColLeftOfMiddle;
}
else if( iColOnSide >= iColRightOfMiddle )
{
iFirstColOnSide = iColRightOfMiddle;
iLastColOnSide = iNumColsPerSide-1;
}
else
{
iFirstColOnSide = iColOnSide/2;
iLastColOnSide = iColOnSide/2;
}
// mirror
int iNewColOnSide;
if( iFirstColOnSide == iLastColOnSide )
iNewColOnSide = 0;
else
iNewColOnSide = SCALE( iColOnSide, iFirstColOnSide, iLastColOnSide, iLastColOnSide, iFirstColOnSide );
const int iNewCol = iSideIndex*iNumColsPerSide + iNewColOnSide;
const float fOldPixelOffset = pCols[iColNum].fXOffset;
const float fNewPixelOffset = pCols[iNewCol].fXOffset;
data.m_fInvertDistance[iColNum] = fNewPixelOffset - fOldPixelOffset;
}
// Update Tipsy
if(effects[PlayerOptions::EFFECT_TIPSY] != 0)
{
2017-07-14 22:32:58 -07:00
UpdateTipsy(data.m_tipsy_result, data.m_tipsy_offset_result,
effects[PlayerOptions::EFFECT_TIPSY_OFFSET],
effects[PlayerOptions::EFFECT_TIPSY_SPEED], false);
}
else
{
for(int col= 0; col < MAX_COLS_PER_PLAYER; ++col)
{
2017-07-14 22:32:58 -07:00
data.m_tipsy_result[col]= 0;
}
}
2017-07-14 22:32:58 -07:00
// Update TanTipsy
if(effects[PlayerOptions::EFFECT_TAN_TIPSY] != 0)
{
UpdateTipsy(data.m_tan_tipsy_result, data.m_tan_tipsy_offset_result,
effects[PlayerOptions::EFFECT_TAN_TIPSY_OFFSET],
effects[PlayerOptions::EFFECT_TAN_TIPSY_SPEED], true);
}
else
{
for(int col= 0; col < MAX_COLS_PER_PLAYER; ++col)
{
2017-07-14 22:32:58 -07:00
data.m_tan_tipsy_result[col]= 0;
}
}
2011-03-17 01:47:30 -04:00
// Update Beat
UpdateBeat(dim_x, data, position, effects[PlayerOptions::EFFECT_BEAT_OFFSET], effects[PlayerOptions::EFFECT_BEAT_MULT]);
2011-03-17 01:47:30 -04:00
// Update BeatY
UpdateBeat(dim_y, data, position, effects[PlayerOptions::EFFECT_BEAT_Y_OFFSET], effects[PlayerOptions::EFFECT_BEAT_Y_MULT]);
2011-03-17 01:47:30 -04:00
// Update BeatZ
UpdateBeat(dim_z, data, position, effects[PlayerOptions::EFFECT_BEAT_Z_OFFSET], effects[PlayerOptions::EFFECT_BEAT_Z_MULT]);
2011-03-17 01:47:30 -04:00
}
fLastTime = fTime;
2011-03-17 01:47:30 -04:00
}
void ArrowEffects::SetCurrentOptions(const PlayerOptions* options)
{
curr_options= options;
}
static float GetDisplayedBeat( const PlayerState* pPlayerState, float beat )
{
// do a binary search here
const vector<CacheDisplayedBeat> &data = pPlayerState->m_CacheDisplayedBeat;
int max = data.size() - 1;
int l = 0, r = max;
while( l <= r )
{
int m = ( l + r ) / 2;
if( ( m == 0 || data[m].beat <= beat ) && ( m == max || beat < data[m + 1].beat ) )
{
return data[m].displayedBeat + data[m].velocity * (beat - data[m].beat);
}
else if( data[m].beat <= beat )
{
l = m + 1;
}
else
{
r = m - 1;
}
}
return beat;
}
2011-03-17 01:47:30 -04:00
/* For visibility testing: if bAbsolute is false, random modifiers must return
* the minimum possible scroll speed. */
float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, float &fPeakYOffsetOut, bool &bIsPastPeakOut, bool bAbsolute )
{
// Default values that are returned if boomerang is off.
fPeakYOffsetOut = FLT_MAX;
bIsPastPeakOut = true;
float fYOffset = 0;
const SongPosition &position = pPlayerState->GetDisplayedPosition();
float fSongBeat = position.m_fSongBeatVisible;
Steps *pCurSteps = GAMESTATE->m_pCurSteps[pPlayerState->m_PlayerNumber];
2011-03-17 01:47:30 -04:00
/* Usually, fTimeSpacing is 0 or 1, in which case we use entirely beat spacing or
* entirely time spacing (respectively). Occasionally, we tween between them. */
if( curr_options->m_fTimeSpacing != 1.0f )
2011-03-17 01:47:30 -04:00
{
2013-07-18 15:52:56 -04:00
if( GAMESTATE->m_bInStepEditor ) {
// Use constant spacing in step editor
fYOffset = fNoteBeat - fSongBeat;
2013-07-18 14:40:16 -04:00
} else {
2013-07-18 15:52:56 -04:00
fYOffset = GetDisplayedBeat(pPlayerState, fNoteBeat) - GetDisplayedBeat(pPlayerState, fSongBeat);
fYOffset *= pCurSteps->GetTimingData()->GetDisplayedSpeedPercent(
2011-06-12 03:37:10 -04:00
position.m_fSongBeatVisible,
2013-07-18 14:40:16 -04:00
position.m_fMusicSecondsVisible );
}
fYOffset *= 1 - curr_options->m_fTimeSpacing;
2011-03-17 01:47:30 -04:00
}
if( curr_options->m_fTimeSpacing != 0.0f )
2011-03-17 01:47:30 -04:00
{
float fSongSeconds = GAMESTATE->m_Position.m_fMusicSecondsVisible;
float fNoteSeconds = pCurSteps->GetTimingData()->GetElapsedTimeFromBeat(fNoteBeat);
2011-03-17 01:47:30 -04:00
float fSecondsUntilStep = fNoteSeconds - fSongSeconds;
float fBPM = curr_options->m_fScrollBPM;
float fBPS = fBPM/60.f / GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate;
2011-03-17 01:47:30 -04:00
float fYOffsetTimeSpacing = fSecondsUntilStep * fBPS;
fYOffset += fYOffsetTimeSpacing * curr_options->m_fTimeSpacing;
2011-03-17 01:47:30 -04:00
}
// TODO: If we allow noteskins to have metricable row spacing
// (per issue 24), edit this to reflect that. -aj
2011-05-15 17:45:21 -04:00
fYOffset *= ARROW_SPACING;
// Factor in scroll speed
float fScrollSpeed = curr_options->m_fScrollSpeed;
if(curr_options->m_fMaxScrollBPM != 0)
{
fScrollSpeed= curr_options->m_fMaxScrollBPM /
(pPlayerState->m_fReadBPM * GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate);
}
2011-05-15 17:45:21 -04:00
2011-03-17 01:47:30 -04:00
// don't mess with the arrows after they've crossed 0
if( fYOffset < 0 )
{
return fYOffset * fScrollSpeed;
}
2011-03-17 01:47:30 -04:00
const float* fAccels = curr_options->m_fAccels;
const float* fEffects = curr_options->m_fEffects;
2017-07-14 22:32:58 -07:00
// TODO: Don't index by PlayerNumber.
PerPlayerData &data = g_EffectData[pPlayerState->m_PlayerNumber];
2011-03-17 01:47:30 -04:00
float fYAdjust = 0; // fill this in depending on PlayerOptions
if( fAccels[PlayerOptions::ACCEL_BOOST] != 0 )
{
float fEffectHeight = GetNoteFieldHeight();
2011-03-17 01:47:30 -04:00
float fNewYOffset = fYOffset * 1.5f / ((fYOffset+fEffectHeight/1.2f)/fEffectHeight);
float fAccelYAdjust = fAccels[PlayerOptions::ACCEL_BOOST] * (fNewYOffset - fYOffset);
// TRICKY: Clamp this value, or else BOOST+BOOMERANG will draw a ton of arrows on the screen.
CLAMP( fAccelYAdjust, BOOST_MOD_MIN_CLAMP, BOOST_MOD_MAX_CLAMP );
fYAdjust += fAccelYAdjust;
}
if( fAccels[PlayerOptions::ACCEL_BRAKE] != 0 )
{
float fEffectHeight = GetNoteFieldHeight();
2011-03-17 01:47:30 -04:00
float fScale = SCALE( fYOffset, 0.f, fEffectHeight, 0, 1.f );
float fNewYOffset = fYOffset * fScale;
float fBrakeYAdjust = fAccels[PlayerOptions::ACCEL_BRAKE] * (fNewYOffset - fYOffset);
// TRICKY: Clamp this value the same way as BOOST so that in BOOST+BRAKE, BRAKE doesn't overpower BOOST
CLAMP( fBrakeYAdjust, BRAKE_MOD_MIN_CLAMP, BRAKE_MOD_MAX_CLAMP );
fYAdjust += fBrakeYAdjust;
}
if( fAccels[PlayerOptions::ACCEL_WAVE] != 0 )
2017-03-14 03:27:18 -07:00
fYAdjust += fAccels[PlayerOptions::ACCEL_WAVE] * WAVE_MOD_MAGNITUDE *RageFastSin( fYOffset/((fAccels[PlayerOptions::ACCEL_WAVE_PERIOD]*WAVE_MOD_HEIGHT)+WAVE_MOD_HEIGHT) );
if( fEffects[PlayerOptions::EFFECT_PARABOLA_Y] != 0 )
fYAdjust += fEffects[PlayerOptions::EFFECT_PARABOLA_Y] * (fYOffset/ARROW_SIZE) * (fYOffset/ARROW_SIZE);
2011-03-17 01:47:30 -04:00
fYOffset += fYAdjust;
// Factor in boomerang
if( fAccels[PlayerOptions::ACCEL_BOOMERANG] != 0 )
{
float fPeakAtYOffset = SCREEN_HEIGHT * BOOMERANG_PEAK_PERCENTAGE; // zero point of boomerang function
fPeakYOffsetOut = (-1*fPeakAtYOffset*fPeakAtYOffset/SCREEN_HEIGHT) + 1.5f*fPeakAtYOffset;
bIsPastPeakOut = fYOffset < fPeakAtYOffset;
fYOffset = (-1*fYOffset*fYOffset/SCREEN_HEIGHT) + 1.5f*fYOffset;
}
if( curr_options->m_fRandomSpeed > 0 && !bAbsolute )
2011-03-17 01:47:30 -04:00
{
// Generate a deterministically "random" speed for each arrow.
unsigned seed = GAMESTATE->m_iStageSeed + ( BeatToNoteRow( fNoteBeat ) << 8 ) + (iCol * 100);
for( int i = 0; i < 3; ++i )
seed = ((seed * 1664525u) + 1013904223u) & 0xFFFFFFFF;
float fRandom = seed / 4294967296.0f;
/* Random speed always increases speed: a random speed of 10 indicates
* [1,11]. This keeps it consistent with other mods: 0 means no effect. */
fScrollSpeed *=
SCALE( fRandom,
0.0f, 1.0f,
1.0f, curr_options->m_fRandomSpeed + 1.0f );
2011-03-17 01:47:30 -04:00
}
if( fAccels[PlayerOptions::ACCEL_EXPAND] != 0 )
{
2017-03-14 03:27:18 -07:00
float fExpandMultiplier = SCALE( RageFastCos(data.m_fExpandSeconds*EXPAND_MULTIPLIER_FREQUENCY*(fAccels[PlayerOptions::ACCEL_EXPAND_PERIOD]+1)),
2011-03-17 01:47:30 -04:00
EXPAND_MULTIPLIER_SCALE_FROM_LOW, EXPAND_MULTIPLIER_SCALE_FROM_HIGH,
EXPAND_MULTIPLIER_SCALE_TO_LOW, EXPAND_MULTIPLIER_SCALE_TO_HIGH );
fScrollSpeed *= SCALE( fAccels[PlayerOptions::ACCEL_EXPAND],
EXPAND_SPEED_SCALE_FROM_LOW, EXPAND_SPEED_SCALE_FROM_HIGH,
EXPAND_SPEED_SCALE_TO_LOW, fExpandMultiplier );
}
2017-07-14 22:32:58 -07:00
if( fAccels[PlayerOptions::ACCEL_TAN_EXPAND] != 0 )
{
float fTanExpandMultiplier = SCALE( SelectTanType(data.m_fTanExpandSeconds*EXPAND_MULTIPLIER_FREQUENCY*(fAccels[PlayerOptions::ACCEL_TAN_EXPAND_PERIOD]+1), curr_options->m_bCosecant),
EXPAND_MULTIPLIER_SCALE_FROM_LOW, EXPAND_MULTIPLIER_SCALE_FROM_HIGH,
EXPAND_MULTIPLIER_SCALE_TO_LOW, EXPAND_MULTIPLIER_SCALE_TO_HIGH );
fScrollSpeed *= SCALE( fAccels[PlayerOptions::ACCEL_TAN_EXPAND],
EXPAND_SPEED_SCALE_FROM_LOW, EXPAND_SPEED_SCALE_FROM_HIGH,
EXPAND_SPEED_SCALE_TO_LOW, fTanExpandMultiplier );
}
2011-03-17 01:47:30 -04:00
fYOffset *= fScrollSpeed;
fPeakYOffsetOut *= fScrollSpeed;
return fYOffset;
}
static void ArrowGetReverseShiftAndScale(int iCol, float fYReverseOffsetPixels, float &fShiftOut, float &fScaleOut)
2011-03-17 01:47:30 -04:00
{
// XXX: Hack: we need to scale the reverse shift by the zoom.
float fMiniPercent = curr_options->m_fEffects[PlayerOptions::EFFECT_MINI];
2011-07-05 16:50:06 -04:00
float fZoom = 1 - fMiniPercent*0.5f;
2011-03-17 01:47:30 -04:00
// don't divide by 0
if( fabsf(fZoom) < 0.01 )
fZoom = 0.01f;
float fPercentReverse = curr_options->GetReversePercentForColumn(iCol);
2011-03-17 01:47:30 -04:00
fShiftOut = SCALE( fPercentReverse, 0.f, 1.f, -fYReverseOffsetPixels/fZoom/2, fYReverseOffsetPixels/fZoom/2 );
float fPercentCentered = curr_options->m_fScrolls[PlayerOptions::SCROLL_CENTERED];
2011-03-17 01:47:30 -04:00
fShiftOut = SCALE( fPercentCentered, 0.f, 1.f, fShiftOut, 0.0f );
fScaleOut = SCALE( fPercentReverse, 0.f, 1.f, 1.f, -1.f );
}
float ArrowEffects::GetYPos( const PlayerState* pPlayerState, int iCol, float fYOffset, float fYReverseOffsetPixels, bool WithReverse)
2011-03-17 01:47:30 -04:00
{
float f = fYOffset;
if( WithReverse )
{
float fShift, fScale;
ArrowGetReverseShiftAndScale(iCol, fYReverseOffsetPixels, fShift, fScale);
2011-03-17 01:47:30 -04:00
f *= fScale;
f += fShift;
}
// TODO: Don't index by PlayerNumber.
const Style* pStyle = GAMESTATE->GetCurrentStyle(pPlayerState->m_PlayerNumber);
const Style::ColumnInfo* pCols = pStyle->m_ColumnInfo[pPlayerState->m_PlayerNumber];
const float* fEffects = curr_options->m_fEffects;
// Doing the math with a precalculated result of 0 should be faster than
// checking whether tipsy is on. -Kyz
// TODO: Don't index by PlayerNumber.
PerPlayerData& data= g_EffectData[curr_options->m_pn];
f+= fEffects[PlayerOptions::EFFECT_TIPSY] * data.m_tipsy_result[iCol];
2017-07-14 22:32:58 -07:00
f+= fEffects[PlayerOptions::EFFECT_TAN_TIPSY] * data.m_tan_tipsy_result[iCol];
if( fEffects[PlayerOptions::EFFECT_ATTENUATE_Y] != 0 )
{
const float fXOffset = pCols[iCol].fXOffset;
f += fEffects[PlayerOptions::EFFECT_ATTENUATE_Y] * (fYOffset/ARROW_SIZE) * (fYOffset/ARROW_SIZE) * (fXOffset/ARROW_SIZE);
}
2011-03-17 01:47:30 -04:00
if( fEffects[PlayerOptions::EFFECT_BEAT_Y] != 0 )
{
const float fShift = data.m_fBeatFactor[dim_y]*RageFastSin( fYOffset / ((fEffects[PlayerOptions::EFFECT_BEAT_Y_PERIOD]*BEAT_Y_OFFSET_HEIGHT)+BEAT_Y_OFFSET_HEIGHT) + PI/BEAT_Y_PI_HEIGHT );
f += fEffects[PlayerOptions::EFFECT_BEAT_Y] * fShift;
}
2011-03-17 01:47:30 -04:00
// In beware's DDR Extreme-focused fork of StepMania 3.9, this value is
// floored, making arrows show on integer Y coordinates. Supposedly it makes
// the arrows look better, but testing needs to be done.
// todo: make this a noteskin metric instead of a theme metric? -aj
return QUANTIZE_ARROW_Y ? floor(f) : f;
}
float ArrowEffects::GetYOffsetFromYPos(int iCol, float YPos, float fYReverseOffsetPixels)
2011-03-17 01:47:30 -04:00
{
float f = YPos;
const float* fEffects = curr_options->m_fEffects;
// Doing the math with a precalculated result of 0 should be faster than
// checking whether tipsy is on. -Kyz
// TODO: Don't index by PlayerNumber.
PerPlayerData& data= g_EffectData[curr_options->m_pn];
f+= fEffects[PlayerOptions::EFFECT_TIPSY] * data.m_tipsy_offset_result[iCol];
2017-07-14 22:32:58 -07:00
f+= fEffects[PlayerOptions::EFFECT_TAN_TIPSY] * data.m_tan_tipsy_offset_result[iCol];
f+= fEffects[PlayerOptions::EFFECT_PARABOLA_Y] * (YPos/ARROW_SIZE) * (YPos/ARROW_SIZE);
2011-03-17 01:47:30 -04:00
float fShift, fScale;
ArrowGetReverseShiftAndScale(iCol, fYReverseOffsetPixels, fShift, fScale);
2011-03-17 01:47:30 -04:00
f -= fShift;
if( fScale )
f /= fScale;
return f;
}
float ArrowEffects::GetXPos( const PlayerState* pPlayerState, int iColNum, float fYOffset )
{
float fPixelOffsetFromCenter = 0; // fill this in below
const Style* pStyle = GAMESTATE->GetCurrentStyle(pPlayerState->m_PlayerNumber);
const float* fEffects = curr_options->m_fEffects;
2011-03-17 01:47:30 -04:00
// TODO: Don't index by PlayerNumber.
const Style::ColumnInfo* pCols = pStyle->m_ColumnInfo[pPlayerState->m_PlayerNumber];
PerPlayerData &data = g_EffectData[pPlayerState->m_PlayerNumber];
if( fEffects[PlayerOptions::EFFECT_TORNADO] != 0 )
{
fPixelOffsetFromCenter += CalculateTornadoOffsetFromMagnitude(dim_x,
iColNum, fEffects[PlayerOptions::EFFECT_TORNADO],
fEffects[PlayerOptions::EFFECT_TORNADO_OFFSET],
fEffects[PlayerOptions::EFFECT_TORNADO_PERIOD],
2017-07-14 22:32:58 -07:00
pCols, pPlayerState->m_NotefieldZoom, data, fYOffset, false);
}
if( fEffects[PlayerOptions::EFFECT_TAN_TORNADO] != 0 )
{
fPixelOffsetFromCenter += CalculateTornadoOffsetFromMagnitude(dim_x,
iColNum, fEffects[PlayerOptions::EFFECT_TAN_TORNADO],
fEffects[PlayerOptions::EFFECT_TAN_TORNADO_OFFSET],
fEffects[PlayerOptions::EFFECT_TAN_TORNADO_PERIOD],
pCols, pPlayerState->m_NotefieldZoom, data, fYOffset, true);
2011-03-17 01:47:30 -04:00
}
if( fEffects[PlayerOptions::EFFECT_BUMPY_X] != 0 )
2017-07-14 22:32:58 -07:00
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_BUMPY_X] *
40*RageFastSin( CalculateBumpyAngle(fYOffset,
fEffects[PlayerOptions::EFFECT_BUMPY_X_OFFSET],
fEffects[PlayerOptions::EFFECT_BUMPY_X_PERIOD]) );
if( fEffects[PlayerOptions::EFFECT_TAN_BUMPY_X] != 0 )
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_TAN_BUMPY_X] *
40*SelectTanType( CalculateBumpyAngle(fYOffset,
fEffects[PlayerOptions::EFFECT_TAN_BUMPY_X_OFFSET],
fEffects[PlayerOptions::EFFECT_TAN_BUMPY_X_PERIOD]), curr_options->m_bCosecant );
2011-03-17 01:47:30 -04:00
if( fEffects[PlayerOptions::EFFECT_DRUNK] != 0 )
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_DRUNK] *
2017-07-14 22:32:58 -07:00
( RageFastCos( CalculateDrunkAngle(fEffects[PlayerOptions::EFFECT_DRUNK_SPEED], iColNum,
fEffects[PlayerOptions::EFFECT_DRUNK_OFFSET], DRUNK_COLUMN_FREQUENCY,
fYOffset, fEffects[PlayerOptions::EFFECT_DRUNK_PERIOD],
DRUNK_OFFSET_FREQUENCY) ) * ARROW_SIZE*DRUNK_ARROW_MAGNITUDE );
if( fEffects[PlayerOptions::EFFECT_TAN_DRUNK] != 0 )
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_TAN_DRUNK] *
( SelectTanType( CalculateDrunkAngle(fEffects[PlayerOptions::EFFECT_TAN_DRUNK_SPEED],
iColNum, fEffects[PlayerOptions::EFFECT_TAN_DRUNK_OFFSET],
DRUNK_COLUMN_FREQUENCY, fYOffset,
fEffects[PlayerOptions::EFFECT_TAN_DRUNK_PERIOD], DRUNK_OFFSET_FREQUENCY)
, curr_options->m_bCosecant) * ARROW_SIZE*DRUNK_ARROW_MAGNITUDE );
2011-03-17 01:47:30 -04:00
if( fEffects[PlayerOptions::EFFECT_FLIP] != 0 )
{
const int iFirstCol = 0;
const int iLastCol = pStyle->m_iColsPerPlayer-1;
const int iNewCol = SCALE( iColNum, iFirstCol, iLastCol, iLastCol, iFirstCol );
const float fOldPixelOffset = pCols[iColNum].fXOffset * pPlayerState->m_NotefieldZoom;
const float fNewPixelOffset = pCols[iNewCol].fXOffset * pPlayerState->m_NotefieldZoom;
2011-03-17 01:47:30 -04:00
const float fDistance = fNewPixelOffset - fOldPixelOffset;
fPixelOffsetFromCenter += fDistance * fEffects[PlayerOptions::EFFECT_FLIP];
}
if( fEffects[PlayerOptions::EFFECT_INVERT] != 0 )
fPixelOffsetFromCenter += data.m_fInvertDistance[iColNum] * fEffects[PlayerOptions::EFFECT_INVERT];
if( fEffects[PlayerOptions::EFFECT_BEAT] != 0 )
{
const float fShift = data.m_fBeatFactor[dim_x]*RageFastSin( fYOffset / ((fEffects[PlayerOptions::EFFECT_BEAT_PERIOD]*BEAT_OFFSET_HEIGHT)+BEAT_OFFSET_HEIGHT) + PI/BEAT_PI_HEIGHT );
2011-03-17 01:47:30 -04:00
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_BEAT] * fShift;
}
if( fEffects[PlayerOptions::EFFECT_ZIGZAG] != 0 )
{
float fResult = RageTriangle( (PI * (1/(fEffects[PlayerOptions::EFFECT_ZIGZAG_PERIOD]+1)) *
((fYOffset+(100.0f*(fEffects[PlayerOptions::EFFECT_ZIGZAG_OFFSET])))/ARROW_SIZE) ) );
fPixelOffsetFromCenter += (fEffects[PlayerOptions::EFFECT_ZIGZAG]*ARROW_SIZE/2) * fResult;
}
if( fEffects[PlayerOptions::EFFECT_SAWTOOTH] != 0 )
fPixelOffsetFromCenter += (fEffects[PlayerOptions::EFFECT_SAWTOOTH]*ARROW_SIZE) *
((0.5f / (fEffects[PlayerOptions::EFFECT_SAWTOOTH_PERIOD]+1) * fYOffset) / ARROW_SIZE -
floor((0.5f / (fEffects[PlayerOptions::EFFECT_SAWTOOTH_PERIOD]+1) * fYOffset) / ARROW_SIZE) );
if( fEffects[PlayerOptions::EFFECT_PARABOLA_X] != 0 )
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_PARABOLA_X] * (fYOffset/ARROW_SIZE) * (fYOffset/ARROW_SIZE);
if( fEffects[PlayerOptions::EFFECT_ATTENUATE_X] != 0 )
{
const float fXOffset = pCols[iColNum].fXOffset;
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_ATTENUATE_X] * (fYOffset/ARROW_SIZE) * (fYOffset/ARROW_SIZE) * (fXOffset/ARROW_SIZE);
}
if( fEffects[PlayerOptions::EFFECT_DIGITAL] != 0 )
fPixelOffsetFromCenter += (fEffects[PlayerOptions::EFFECT_DIGITAL] * ARROW_SIZE * 0.5f) *
round((fEffects[PlayerOptions::EFFECT_DIGITAL_STEPS]+1) * RageFastSin(
2017-07-14 22:32:58 -07:00
CalculateDigitalAngle(fYOffset,
fEffects[PlayerOptions::EFFECT_DIGITAL_OFFSET],
fEffects[PlayerOptions::EFFECT_DIGITAL_PERIOD]) ) )/(fEffects[PlayerOptions::EFFECT_DIGITAL_STEPS]+1);
if( fEffects[PlayerOptions::EFFECT_TAN_DIGITAL] != 0 )
fPixelOffsetFromCenter += (fEffects[PlayerOptions::EFFECT_TAN_DIGITAL] * ARROW_SIZE * 0.5f) *
round((fEffects[PlayerOptions::EFFECT_TAN_DIGITAL_STEPS]+1) * SelectTanType(
CalculateDigitalAngle(fYOffset,
fEffects[PlayerOptions::EFFECT_TAN_DIGITAL_OFFSET],
fEffects[PlayerOptions::EFFECT_TAN_DIGITAL_PERIOD]), curr_options->m_bCosecant ) )/(fEffects[PlayerOptions::EFFECT_TAN_DIGITAL_STEPS]+1);
if( fEffects[PlayerOptions::EFFECT_SQUARE] != 0 )
{
float fResult = RageSquare( (PI * (fYOffset+(1.0f*(fEffects[PlayerOptions::EFFECT_SQUARE_OFFSET]))) /
(ARROW_SIZE+(fEffects[PlayerOptions::EFFECT_SQUARE_PERIOD]*ARROW_SIZE))) );
fPixelOffsetFromCenter += (fEffects[PlayerOptions::EFFECT_SQUARE] * ARROW_SIZE * 0.5f) * fResult;
}
2011-03-17 01:47:30 -04:00
if( fEffects[PlayerOptions::EFFECT_BOUNCE] != 0 )
{
float fBounceAmt = fabsf( RageFastSin( ( (fYOffset + (1.0f * (fEffects[PlayerOptions::EFFECT_BOUNCE_OFFSET]) ) ) /
( 60 + (fEffects[PlayerOptions::EFFECT_BOUNCE_PERIOD]*60) ) ) ) );
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_BOUNCE] * ARROW_SIZE * 0.5f * fBounceAmt;
}
2011-03-17 01:47:30 -04:00
if( fEffects[PlayerOptions::EFFECT_XMODE] != 0 )
{
// based off of code by v1toko for StepNXA, except it should work on
// any gametype now.
switch( pStyle->m_StyleType )
{
case StyleType_OnePlayerTwoSides:
case StyleType_TwoPlayersSharedSides: // fall through?
{
// find the middle, and split based on iColNum
// it's unknown if this will work for routine.
const int iMiddleColumn = static_cast<int>(floor(pStyle->m_iColsPerPlayer/2.0f));
if( iColNum > iMiddleColumn-1 )
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*-(fYOffset);
else
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*fYOffset;
}
break;
case StyleType_OnePlayerOneSide:
case StyleType_TwoPlayersTwoSides: // fall through
{
// the code was the same for both of these cases in StepNXA.
if( pPlayerState->m_PlayerNumber == PLAYER_2 )
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*-(fYOffset);
else
fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*fYOffset;
}
break;
DEFAULT_FAIL(pStyle->m_StyleType);
}
}
fPixelOffsetFromCenter += pCols[iColNum].fXOffset * pPlayerState->m_NotefieldZoom;
2011-03-17 01:47:30 -04:00
2011-07-05 16:50:06 -04:00
if( fEffects[PlayerOptions::EFFECT_TINY] != 0 )
2011-03-17 01:47:30 -04:00
{
2011-07-05 17:04:20 -04:00
// Allow Tiny to pull tracks together, but not to push them apart.
2011-07-05 16:50:06 -04:00
float fTinyPercent = fEffects[PlayerOptions::EFFECT_TINY];
fTinyPercent = min( powf(TINY_PERCENT_BASE, fTinyPercent), (float)TINY_PERCENT_GATE );
fPixelOffsetFromCenter *= fTinyPercent;
2011-03-17 01:47:30 -04:00
}
return fPixelOffsetFromCenter;
}
float ArrowEffects::GetRotationX(const PlayerState* pPlayerState, float fYOffset, bool bIsHoldCap, int iCol)
2011-03-17 01:47:30 -04:00
{
const float* fEffects = curr_options->m_fEffects;
2011-03-17 01:47:30 -04:00
float fRotation = 0;
if( fEffects[PlayerOptions::EFFECT_CONFUSION_X] != 0 || fEffects[PlayerOptions::EFFECT_CONFUSION_X_OFFSET] != 0 ||
curr_options->m_fConfusionX[iCol] != 0
)
fRotation += ReceptorGetRotationX( pPlayerState, iCol );
2017-02-08 05:44:33 -08:00
if( fEffects[PlayerOptions::EFFECT_ROLL] != 0 && !bIsHoldCap )
2011-03-17 01:47:30 -04:00
{
2017-02-08 05:44:33 -08:00
fRotation += fEffects[PlayerOptions::EFFECT_ROLL] * fYOffset/2;
2011-03-17 01:47:30 -04:00
}
return fRotation;
}
float ArrowEffects::GetRotationY(const PlayerState* pPlayerState, float fYOffset, int iCol)
2011-03-17 01:47:30 -04:00
{
const float* fEffects = curr_options->m_fEffects;
2011-03-17 01:47:30 -04:00
float fRotation = 0;
if( fEffects[PlayerOptions::EFFECT_CONFUSION_Y] != 0 || fEffects[PlayerOptions::EFFECT_CONFUSION_Y_OFFSET] != 0 ||
curr_options->m_fConfusionY[iCol] != 0
)
fRotation += ReceptorGetRotationY( pPlayerState, iCol );
2011-03-17 01:47:30 -04:00
if( fEffects[PlayerOptions::EFFECT_TWIRL] != 0 )
{
2017-02-08 05:44:33 -08:00
fRotation += fEffects[PlayerOptions::EFFECT_TWIRL] * fYOffset/2;
2011-03-17 01:47:30 -04:00
}
return fRotation;
}
float ArrowEffects::GetRotationZ( const PlayerState* pPlayerState, float fNoteBeat, bool bIsHoldHead, int iCol )
2011-03-17 01:47:30 -04:00
{
const float* fEffects = curr_options->m_fEffects;
2011-03-17 01:47:30 -04:00
float fRotation = 0;
if( fEffects[PlayerOptions::EFFECT_CONFUSION] != 0 || fEffects[PlayerOptions::EFFECT_CONFUSION_OFFSET] != 0 ||
curr_options->m_fConfusionZ[iCol] != 0
)
fRotation += ReceptorGetRotationZ( pPlayerState, iCol );
2011-03-17 01:47:30 -04:00
// As usual, enable dizzy hold heads at your own risk. -Wolfman2000
if( fEffects[PlayerOptions::EFFECT_DIZZY] != 0 && ( curr_options->m_bDizzyHolds || !bIsHoldHead ) )
2011-03-17 01:47:30 -04:00
{
const float fSongBeat = pPlayerState->m_Position.m_fSongBeatVisible;
2011-03-17 01:47:30 -04:00
float fDizzyRotation = fNoteBeat - fSongBeat;
fDizzyRotation *= fEffects[PlayerOptions::EFFECT_DIZZY];
fDizzyRotation = fmodf( fDizzyRotation, 2*PI );
fDizzyRotation *= 180/PI;
fRotation += fDizzyRotation;
}
return fRotation;
}
float ArrowEffects::ReceptorGetRotationZ( const PlayerState* pPlayerState, int iCol )
2011-03-17 01:47:30 -04:00
{
const float* fEffects = curr_options->m_fEffects;
2011-03-17 01:47:30 -04:00
float fRotation = 0;
if( curr_options->m_fConfusionZ[iCol] != 0 )
fRotation += curr_options->m_fConfusionZ[iCol] * 180.0f/PI;
2017-02-08 05:44:33 -08:00
if( fEffects[PlayerOptions::EFFECT_CONFUSION_OFFSET] != 0 )
fRotation += fEffects[PlayerOptions::EFFECT_CONFUSION_OFFSET] * 180.0f/PI;
2011-03-17 01:47:30 -04:00
if( fEffects[PlayerOptions::EFFECT_CONFUSION] != 0 )
{
float fConfRotation = pPlayerState->m_Position.m_fSongBeatVisible;
2011-03-17 01:47:30 -04:00
fConfRotation *= fEffects[PlayerOptions::EFFECT_CONFUSION];
fConfRotation = fmodf( fConfRotation, 2*PI );
fConfRotation *= -180/PI;
fRotation += fConfRotation;
}
2017-02-08 05:44:33 -08:00
return fRotation;
}
float ArrowEffects::ReceptorGetRotationX( const PlayerState* pPlayerState, int iCol )
2017-02-08 05:44:33 -08:00
{
const float* fEffects = curr_options->m_fEffects;
float fRotation = 0;
if( curr_options->m_fConfusionX[iCol] != 0 )
fRotation += curr_options->m_fConfusionX[iCol] * 180.0f/PI;
2017-02-08 05:44:33 -08:00
if( fEffects[PlayerOptions::EFFECT_CONFUSION_X_OFFSET] != 0 )
fRotation += fEffects[PlayerOptions::EFFECT_CONFUSION_X_OFFSET] * 180.0f/PI;
if( fEffects[PlayerOptions::EFFECT_CONFUSION_X] != 0 )
{
float fConfRotation = pPlayerState->m_Position.m_fSongBeatVisible;
fConfRotation *= fEffects[PlayerOptions::EFFECT_CONFUSION_X];
fConfRotation = fmodf( fConfRotation, 2*PI );
fConfRotation *= -180/PI;
fRotation += fConfRotation;
}
return fRotation;
}
float ArrowEffects::ReceptorGetRotationY( const PlayerState* pPlayerState, int iCol )
2017-02-08 05:44:33 -08:00
{
const float* fEffects = curr_options->m_fEffects;
float fRotation = 0;
if( curr_options->m_fConfusionY[iCol] != 0 )
fRotation += curr_options->m_fConfusionY[iCol] * 180.0f/PI;
2017-02-08 05:44:33 -08:00
if( fEffects[PlayerOptions::EFFECT_CONFUSION_Y_OFFSET] != 0 )
fRotation += fEffects[PlayerOptions::EFFECT_CONFUSION_Y_OFFSET] * 180.0f/PI;
if( fEffects[PlayerOptions::EFFECT_CONFUSION_Y] != 0 )
{
float fConfRotation = pPlayerState->m_Position.m_fSongBeatVisible;
fConfRotation *= fEffects[PlayerOptions::EFFECT_CONFUSION_Y];
fConfRotation = fmodf( fConfRotation, 2*PI );
fConfRotation *= -180/PI;
fRotation += fConfRotation;
}
2011-03-17 01:47:30 -04:00
return fRotation;
}
2017-02-25 15:48:03 -08:00
float ArrowEffects::GetMoveX(int iCol)
{
const float* fMoves = curr_options->m_fMovesX;
float f = 0;
if( fMoves[iCol] != 0 )
f += ARROW_SIZE * fMoves[iCol];
return f;
}
float ArrowEffects::GetMoveY(int iCol)
{
const float* fMoves = curr_options->m_fMovesY;
float f = 0;
if( fMoves[iCol] != 0 )
f += ARROW_SIZE * fMoves[iCol];
return f;
}
float ArrowEffects::GetMoveZ(int iCol)
{
const float* fMoves = curr_options->m_fMovesZ;
float f = 0;
if( fMoves[iCol] != 0 )
f += ARROW_SIZE * fMoves[iCol];
return f;
}
2011-03-17 01:47:30 -04:00
#define CENTER_LINE_Y 160 // from fYOffset == 0
#define FADE_DIST_Y 40
static float GetCenterLine()
2011-03-17 01:47:30 -04:00
{
/* Another mini hack: if EFFECT_MINI is on, then our center line is at
* eg. 320, not 160. */
const float fMiniPercent = curr_options->m_fEffects[PlayerOptions::EFFECT_MINI];
2011-03-17 01:47:30 -04:00
const float fZoom = 1 - fMiniPercent*0.5f;
return CENTER_LINE_Y / fZoom;
}
static float GetHiddenSudden()
2011-03-17 01:47:30 -04:00
{
const float* fAppearances = curr_options->m_fAppearances;
2011-03-17 01:47:30 -04:00
return fAppearances[PlayerOptions::APPEARANCE_HIDDEN] *
fAppearances[PlayerOptions::APPEARANCE_SUDDEN];
}
//
// -gray arrows-
//
// ...invisible...
// -hidden end line-
// -hidden start line-
// ...visible...
// -sudden end line-
// -sudden start line-
// ...invisible...
//
// TRICKY: We fudge hidden and sudden to be farther apart if they're both on.
static float GetHiddenEndLine()
2011-03-17 01:47:30 -04:00
{
return GetCenterLine() +
FADE_DIST_Y * SCALE( GetHiddenSudden(), 0.f, 1.f, -1.0f, -1.25f ) +
GetCenterLine() * curr_options->m_fAppearances[PlayerOptions::APPEARANCE_HIDDEN_OFFSET];
2011-03-17 01:47:30 -04:00
}
static float GetHiddenStartLine()
2011-03-17 01:47:30 -04:00
{
return GetCenterLine() +
FADE_DIST_Y * SCALE( GetHiddenSudden(), 0.f, 1.f, +0.0f, -0.25f ) +
GetCenterLine() * curr_options->m_fAppearances[PlayerOptions::APPEARANCE_HIDDEN_OFFSET];
2011-03-17 01:47:30 -04:00
}
static float GetSuddenEndLine()
2011-03-17 01:47:30 -04:00
{
return GetCenterLine() +
FADE_DIST_Y * SCALE( GetHiddenSudden(), 0.f, 1.f, -0.0f, +0.25f ) +
GetCenterLine() * curr_options->m_fAppearances[PlayerOptions::APPEARANCE_SUDDEN_OFFSET];
2011-03-17 01:47:30 -04:00
}
static float GetSuddenStartLine()
2011-03-17 01:47:30 -04:00
{
return GetCenterLine() +
FADE_DIST_Y * SCALE( GetHiddenSudden(), 0.f, 1.f, +1.0f, +1.25f ) +
GetCenterLine() * curr_options->m_fAppearances[PlayerOptions::APPEARANCE_SUDDEN_OFFSET];
2011-03-17 01:47:30 -04:00
}
// used by ArrowGetAlpha and ArrowGetGlow below
float ArrowGetPercentVisible(float fYPosWithoutReverse, int iCol, float fYOffset)
2011-03-17 01:47:30 -04:00
{
const float fDistFromCenterLine = fYPosWithoutReverse - GetCenterLine();
2011-03-17 01:47:30 -04:00
float fYPos;
if( curr_options->m_bStealthType )
fYPos = fYOffset;
else
fYPos = fYPosWithoutReverse;
if( fYPos < 0 && curr_options->m_bStealthPastReceptors == false) // past Gray Arrows
2011-03-17 01:47:30 -04:00
return 1; // totally visible
const float* fAppearances = curr_options->m_fAppearances;
2011-03-17 01:47:30 -04:00
float fVisibleAdjust = 0;
if( fAppearances[PlayerOptions::APPEARANCE_HIDDEN] != 0 )
{
float fHiddenVisibleAdjust = SCALE( fYPos, GetHiddenStartLine(), GetHiddenEndLine(), 0, -1 );
2011-03-17 01:47:30 -04:00
CLAMP( fHiddenVisibleAdjust, -1, 0 );
fVisibleAdjust += fAppearances[PlayerOptions::APPEARANCE_HIDDEN] * fHiddenVisibleAdjust;
}
if( fAppearances[PlayerOptions::APPEARANCE_SUDDEN] != 0 )
{
float fSuddenVisibleAdjust = SCALE( fYPos, GetSuddenStartLine(), GetSuddenEndLine(), -1, 0 );
2011-03-17 01:47:30 -04:00
CLAMP( fSuddenVisibleAdjust, -1, 0 );
fVisibleAdjust += fAppearances[PlayerOptions::APPEARANCE_SUDDEN] * fSuddenVisibleAdjust;
}
if( fAppearances[PlayerOptions::APPEARANCE_STEALTH] != 0 )
fVisibleAdjust -= fAppearances[PlayerOptions::APPEARANCE_STEALTH];
if( curr_options->m_fStealth[iCol] != 0 ){
fVisibleAdjust -= curr_options->m_fStealth[iCol];
}
2011-03-17 01:47:30 -04:00
if( fAppearances[PlayerOptions::APPEARANCE_BLINK] != 0 )
{
float f = RageFastSin(ArrowEffects::GetTime()*10);
2011-03-17 01:47:30 -04:00
f = Quantize( f, BLINK_MOD_FREQUENCY );
fVisibleAdjust += SCALE( f, 0, 1, -1, 0 );
}
if( fAppearances[PlayerOptions::APPEARANCE_RANDOMVANISH] != 0 )
{
const float fRealFadeDist = 80;
fVisibleAdjust += SCALE( fabsf(fDistFromCenterLine), fRealFadeDist, 2*fRealFadeDist, -1, 0 )
* fAppearances[PlayerOptions::APPEARANCE_RANDOMVANISH];
}
return clamp( 1+fVisibleAdjust, 0, 1 );
}
float ArrowEffects::GetAlpha( const PlayerState* pPlayerState, int iCol, float fYOffset, float fPercentFadeToFail, float fYReverseOffsetPixels, float fDrawDistanceBeforeTargetsPixels, float fFadeInPercentOfDrawFar)
2011-03-17 01:47:30 -04:00
{
// Get the YPos without reverse (that is, factor in EFFECT_TIPSY).
float fYPosWithoutReverse = ArrowEffects::GetYPos(pPlayerState, iCol, fYOffset, fYReverseOffsetPixels, false );
2011-03-17 01:47:30 -04:00
float fPercentVisible = ArrowGetPercentVisible(fYPosWithoutReverse, iCol, fYOffset);
2011-03-17 01:47:30 -04:00
if( fPercentFadeToFail != -1 )
fPercentVisible = 1 - fPercentFadeToFail;
float fFullAlphaY = fDrawDistanceBeforeTargetsPixels*(1-fFadeInPercentOfDrawFar);
if( fYPosWithoutReverse > fFullAlphaY )
{
float f = SCALE( fYPosWithoutReverse, fFullAlphaY, fDrawDistanceBeforeTargetsPixels, 1.0f, 0.0f );
return f;
}
return (fPercentVisible>0.5f) ? 1.0f : 0.0f;
}
float ArrowEffects::GetGlow( const PlayerState* pPlayerState, int iCol, float fYOffset, float fPercentFadeToFail, float fYReverseOffsetPixels, float fDrawDistanceBeforeTargetsPixels, float fFadeInPercentOfDrawFar)
2011-03-17 01:47:30 -04:00
{
// Get the YPos without reverse (that is, factor in EFFECT_TIPSY).
float fYPosWithoutReverse = ArrowEffects::GetYPos(pPlayerState, iCol, fYOffset, fYReverseOffsetPixels, false );
2011-03-17 01:47:30 -04:00
float fPercentVisible = ArrowGetPercentVisible(fYPosWithoutReverse, iCol, fYOffset);
2011-03-17 01:47:30 -04:00
if( fPercentFadeToFail != -1 )
fPercentVisible = 1 - fPercentFadeToFail;
const float fDistFromHalf = fabsf( fPercentVisible - 0.5f );
return SCALE( fDistFromHalf, 0, 0.5f, 1.3f, 0 );
}
float ArrowEffects::GetBrightness( const PlayerState* pPlayerState, float fNoteBeat )
{
if( GAMESTATE->IsEditing() )
return 1;
float fSongBeat = pPlayerState->m_Position.m_fSongBeatVisible;
2011-03-17 01:47:30 -04:00
float fBeatsUntilStep = fNoteBeat - fSongBeat;
float fBrightness = SCALE( fBeatsUntilStep, 0, -1, 1.f, 0.f );
CLAMP( fBrightness, 0, 1 );
return fBrightness;
}
float ArrowEffects::GetZPos( const PlayerState* pPlayerState, int iCol, float fYOffset)
2011-03-17 01:47:30 -04:00
{
float fZPos=0;
const float* fEffects = curr_options->m_fEffects;
const Style* pStyle = GAMESTATE->GetCurrentStyle(pPlayerState->m_PlayerNumber);
// TODO: Don't index by PlayerNumber.
const Style::ColumnInfo* pCols = pStyle->m_ColumnInfo[pPlayerState->m_PlayerNumber];
PerPlayerData &data = g_EffectData[pPlayerState->m_PlayerNumber];
2011-03-17 01:47:30 -04:00
if( fEffects[PlayerOptions::EFFECT_TORNADO_Z] != 0 )
{
fZPos += CalculateTornadoOffsetFromMagnitude(dim_z, iCol,
fEffects[PlayerOptions::EFFECT_TORNADO_Z],
fEffects[PlayerOptions::EFFECT_TORNADO_Z_OFFSET],
fEffects[PlayerOptions::EFFECT_TORNADO_Z_PERIOD],
2017-07-14 22:32:58 -07:00
pCols, pPlayerState->m_NotefieldZoom, data, fYOffset, false);
}
if( fEffects[PlayerOptions::EFFECT_TAN_TORNADO_Z] != 0 )
{
fZPos += CalculateTornadoOffsetFromMagnitude(dim_z, iCol,
fEffects[PlayerOptions::EFFECT_TAN_TORNADO_Z],
fEffects[PlayerOptions::EFFECT_TAN_TORNADO_Z_OFFSET],
fEffects[PlayerOptions::EFFECT_TAN_TORNADO_Z_PERIOD],
pCols, pPlayerState->m_NotefieldZoom, data, fYOffset, true);
}
2011-03-17 01:47:30 -04:00
if( fEffects[PlayerOptions::EFFECT_BUMPY] != 0 )
2017-07-14 22:32:58 -07:00
fZPos += fEffects[PlayerOptions::EFFECT_BUMPY] * 40*RageFastSin(
CalculateBumpyAngle(fYOffset,
fEffects[PlayerOptions::EFFECT_BUMPY_OFFSET],
fEffects[PlayerOptions::EFFECT_BUMPY_PERIOD]) );
if( curr_options->m_fBumpy[iCol] != 0 )
2017-07-14 22:32:58 -07:00
fZPos += curr_options->m_fBumpy[iCol] * 40*RageFastSin(
CalculateBumpyAngle(fYOffset,
fEffects[PlayerOptions::EFFECT_BUMPY_OFFSET],
fEffects[PlayerOptions::EFFECT_BUMPY_PERIOD]) );
if( fEffects[PlayerOptions::EFFECT_TAN_BUMPY] != 0 )
fZPos += fEffects[PlayerOptions::EFFECT_TAN_BUMPY] * 40*SelectTanType(
CalculateBumpyAngle(fYOffset,
fEffects[PlayerOptions::EFFECT_TAN_BUMPY_OFFSET],
fEffects[PlayerOptions::EFFECT_TAN_BUMPY_PERIOD]), curr_options->m_bCosecant );
if( fEffects[PlayerOptions::EFFECT_ZIGZAG_Z] != 0 )
{
float fResult = RageTriangle( (PI * (1/(fEffects[PlayerOptions::EFFECT_ZIGZAG_Z_PERIOD]+1)) *
((fYOffset+(100.0f*(fEffects[PlayerOptions::EFFECT_ZIGZAG_Z_OFFSET])))/ARROW_SIZE) ) );
fZPos += (fEffects[PlayerOptions::EFFECT_ZIGZAG_Z]*ARROW_SIZE/2) * fResult;
}
if( fEffects[PlayerOptions::EFFECT_SAWTOOTH_Z] != 0 )
fZPos += (fEffects[PlayerOptions::EFFECT_SAWTOOTH_Z]*ARROW_SIZE) *
((0.5f/(fEffects[PlayerOptions::EFFECT_SAWTOOTH_Z_PERIOD]+1)*fYOffset)/ARROW_SIZE -
floor((0.5f/(fEffects[PlayerOptions::EFFECT_SAWTOOTH_Z_PERIOD]+1)*fYOffset)/ARROW_SIZE));
if( fEffects[PlayerOptions::EFFECT_PARABOLA_Z] != 0 )
fZPos += fEffects[PlayerOptions::EFFECT_PARABOLA_Z] * (fYOffset/ARROW_SIZE) * (fYOffset/ARROW_SIZE);
if( fEffects[PlayerOptions::EFFECT_ATTENUATE_Z] != 0 )
{
const float fXOffset = pCols[iCol].fXOffset;
fZPos += fEffects[PlayerOptions::EFFECT_ATTENUATE_Z] * (fYOffset/ARROW_SIZE) * (fYOffset/ARROW_SIZE) * (fXOffset/ARROW_SIZE);
}
if( fEffects[PlayerOptions::EFFECT_DRUNK_Z] != 0 )
fZPos += fEffects[PlayerOptions::EFFECT_DRUNK_Z] *
2017-07-14 22:32:58 -07:00
( RageFastCos( CalculateDrunkAngle(fEffects[PlayerOptions::EFFECT_DRUNK_Z_SPEED], iCol,
fEffects[PlayerOptions::EFFECT_DRUNK_Z_OFFSET], DRUNK_Z_COLUMN_FREQUENCY,
fYOffset, fEffects[PlayerOptions::EFFECT_DRUNK_Z_PERIOD],
DRUNK_Z_OFFSET_FREQUENCY) ) * ARROW_SIZE*DRUNK_Z_ARROW_MAGNITUDE );
if( fEffects[PlayerOptions::EFFECT_TAN_DRUNK_Z] != 0 )
fZPos += fEffects[PlayerOptions::EFFECT_TAN_DRUNK_Z] *
( SelectTanType( CalculateDrunkAngle(fEffects[PlayerOptions::EFFECT_TAN_DRUNK_Z_SPEED],
iCol, fEffects[PlayerOptions::EFFECT_TAN_DRUNK_Z_OFFSET],
DRUNK_Z_COLUMN_FREQUENCY, fYOffset,
fEffects[PlayerOptions::EFFECT_TAN_DRUNK_Z_PERIOD],
DRUNK_Z_OFFSET_FREQUENCY)
, curr_options->m_bCosecant) * ARROW_SIZE*DRUNK_Z_ARROW_MAGNITUDE );
if( fEffects[PlayerOptions::EFFECT_BEAT_Z] != 0 )
{
const float fShift = data.m_fBeatFactor[dim_z]*RageFastSin( fYOffset / ((fEffects[PlayerOptions::EFFECT_BEAT_Z_PERIOD]*BEAT_Z_OFFSET_HEIGHT)+BEAT_Z_OFFSET_HEIGHT) + PI/BEAT_Z_PI_HEIGHT );
fZPos += fEffects[PlayerOptions::EFFECT_BEAT_Z] * fShift;
}
if( fEffects[PlayerOptions::EFFECT_DIGITAL_Z] != 0 )
fZPos += (fEffects[PlayerOptions::EFFECT_DIGITAL_Z] * ARROW_SIZE * 0.5f) *
round((fEffects[PlayerOptions::EFFECT_DIGITAL_Z_STEPS]+1) * RageFastSin(
2017-07-14 22:32:58 -07:00
CalculateDigitalAngle(fYOffset,
fEffects[PlayerOptions::EFFECT_DIGITAL_Z_OFFSET],
fEffects[PlayerOptions::EFFECT_DIGITAL_Z_PERIOD]) ) ) /(fEffects[PlayerOptions::EFFECT_DIGITAL_Z_STEPS]+1);
if( fEffects[PlayerOptions::EFFECT_TAN_DIGITAL_Z] != 0 )
fZPos += (fEffects[PlayerOptions::EFFECT_TAN_DIGITAL_Z] * ARROW_SIZE * 0.5f) *
round((fEffects[PlayerOptions::EFFECT_TAN_DIGITAL_Z_STEPS]+1) * SelectTanType(
CalculateDigitalAngle(fYOffset,
fEffects[PlayerOptions::EFFECT_TAN_DIGITAL_Z_OFFSET],
fEffects[PlayerOptions::EFFECT_TAN_DIGITAL_Z_PERIOD]), curr_options->m_bCosecant ) ) /(fEffects[PlayerOptions::EFFECT_TAN_DIGITAL_Z_STEPS]+1);
if( fEffects[PlayerOptions::EFFECT_SQUARE_Z] != 0 )
{
float fResult = RageSquare( (PI * (fYOffset+(1.0f*(fEffects[PlayerOptions::EFFECT_SQUARE_Z_OFFSET]))) /
(ARROW_SIZE+(fEffects[PlayerOptions::EFFECT_SQUARE_Z_PERIOD]*ARROW_SIZE))) );
fZPos += (fEffects[PlayerOptions::EFFECT_SQUARE_Z] * ARROW_SIZE * 0.5f) * fResult;
}
2011-03-17 01:47:30 -04:00
if( fEffects[PlayerOptions::EFFECT_BOUNCE_Z] != 0 )
{
float fBounceAmt = fabsf( RageFastSin( ( (fYOffset + (1.0f * (fEffects[PlayerOptions::EFFECT_BOUNCE_Z_OFFSET]) ) ) /
( 60 + (fEffects[PlayerOptions::EFFECT_BOUNCE_Z_PERIOD]*60) ) ) ) );
fZPos += fEffects[PlayerOptions::EFFECT_BOUNCE_Z] * ARROW_SIZE * 0.5f * fBounceAmt;
}
2011-03-17 01:47:30 -04:00
return fZPos;
}
bool ArrowEffects::NeedZBuffer()
2011-03-17 01:47:30 -04:00
{
const float* fEffects = curr_options->m_fEffects;
2011-03-17 01:47:30 -04:00
// We also need to use the Z buffer if twirl is in play, because of
// hold modulation. -vyhd (OpenITG r623)
if( fEffects[PlayerOptions::EFFECT_BUMPY] != 0 ||
fEffects[PlayerOptions::EFFECT_TWIRL] != 0 )
{
2011-03-17 01:47:30 -04:00
return true;
}
if( fEffects[PlayerOptions::EFFECT_BEAT_Z] != 0 ||
fEffects[PlayerOptions::EFFECT_DIGITAL_Z] != 0 )
{
return true;
}
if( fEffects[PlayerOptions::EFFECT_ZIGZAG_Z] != 0 ||
fEffects[PlayerOptions::EFFECT_SAWTOOTH_Z] != 0 )
{
return true;
}
if( fEffects[PlayerOptions::EFFECT_PARABOLA_Z] != 0 ||
fEffects[PlayerOptions::EFFECT_SQUARE_Z] != 0 )
{
return true;
}
if( curr_options->m_bZBuffer ||
fEffects[PlayerOptions::EFFECT_ATTENUATE_Z] != 0 )
{
return true;
}
if( fEffects[PlayerOptions::EFFECT_BOUNCE_Z] != 0 )
{
return true;
}
2011-03-17 01:47:30 -04:00
return false;
}
float ArrowEffects::GetZoom( const PlayerState* pPlayerState, float fYOffset, int iCol )
2011-03-17 01:47:30 -04:00
{
float fZoom = 1.0f;
// Design change: Instead of having a flag in the style that toggles a
// fixed zoom (0.6) that is only applied to the columns, ScreenGameplay now
// calculates a zoom factor to apply to the notefield and puts it in the
// PlayerState. -Kyz
fZoom*= pPlayerState->m_NotefieldZoom;
fZoom = GetZoomVariable( fYOffset, iCol, fZoom);
2011-03-17 01:47:30 -04:00
float fTinyPercent = curr_options->m_fEffects[PlayerOptions::EFFECT_TINY];
2011-07-05 16:50:06 -04:00
if( fTinyPercent != 0 )
2011-03-17 01:47:30 -04:00
{
2011-07-05 16:50:06 -04:00
fTinyPercent = powf( 0.5f, fTinyPercent );
fZoom *= fTinyPercent;
2011-03-17 01:47:30 -04:00
}
if( curr_options->m_fTiny[iCol] != 0 )
{
fTinyPercent = powf( 0.5f, curr_options->m_fTiny[iCol] );
fZoom *= fTinyPercent;
}
2011-03-17 01:47:30 -04:00
return fZoom;
}
float ArrowEffects::GetZoomVariable( float fYOffset, int iCol, float fCurZoom )
{
float fZoom = fCurZoom;
if( curr_options->m_fEffects[PlayerOptions::EFFECT_PULSE_INNER] != 0 || curr_options->m_fEffects[PlayerOptions::EFFECT_PULSE_OUTER] != 0 )
{
float sine = RageFastSin(((fYOffset+(100.0f*(curr_options->m_fEffects[PlayerOptions::EFFECT_PULSE_OFFSET])))/(0.4f*(ARROW_SIZE+(curr_options->m_fEffects[PlayerOptions::EFFECT_PULSE_PERIOD]*ARROW_SIZE)))));
fZoom *= (sine*(curr_options->m_fEffects[PlayerOptions::EFFECT_PULSE_OUTER]*0.5f))+GetPulseInner();
}
if( curr_options->m_fEffects[PlayerOptions::EFFECT_SHRINK_TO_MULT] !=0 && fYOffset >= 0 )
fZoom *= 1/(1+(fYOffset*(curr_options->m_fEffects[PlayerOptions::EFFECT_SHRINK_TO_MULT]/100.0f)));
if( curr_options->m_fEffects[PlayerOptions::EFFECT_SHRINK_TO_LINEAR] !=0 && fYOffset >= 0 )
fZoom += fYOffset*(0.5f*curr_options->m_fEffects[PlayerOptions::EFFECT_SHRINK_TO_LINEAR]/ARROW_SIZE);
return fZoom;
}
float ArrowEffects::GetPulseInner()
{
float fPulseInner = 1.0f;
if( curr_options->m_fEffects[PlayerOptions::EFFECT_PULSE_INNER] != 0 || curr_options->m_fEffects[PlayerOptions::EFFECT_PULSE_OUTER] != 0 )
{
fPulseInner = ((curr_options->m_fEffects[PlayerOptions::EFFECT_PULSE_INNER]*0.5f)+1);
if (fPulseInner == 0)
{
fPulseInner = 0.01f;
}
}
return fPulseInner;
}
2011-03-17 01:47:30 -04:00
static ThemeMetric<float> FRAME_WIDTH_EFFECTS_PIXELS_PER_SECOND( "ArrowEffects", "FrameWidthEffectsPixelsPerSecond" );
static ThemeMetric<float> FRAME_WIDTH_EFFECTS_MIN_MULTIPLIER( "ArrowEffects", "FrameWidthEffectsMinMultiplier" );
static ThemeMetric<float> FRAME_WIDTH_EFFECTS_MAX_MULTIPLIER( "ArrowEffects", "FrameWidthEffectsMaxMultiplier" );
static ThemeMetric<bool> FRAME_WIDTH_LOCK_EFFECTS_TO_OVERLAPPING( "ArrowEffects", "FrameWidthLockEffectsToOverlapping" );
static ThemeMetric<float> FRAME_WIDTH_LOCK_EFFECTS_TWEEN_PIXELS( "ArrowEffects", "FrameWidthLockEffectsTweenPixels" );
float ArrowEffects::GetFrameWidthScale( const PlayerState* pPlayerState, float fYOffset, float fOverlappedTime )
{
float fFrameWidthMultiplier = 1.0f;
float fPixelsPerSecond = FRAME_WIDTH_EFFECTS_PIXELS_PER_SECOND;
float fSecond = fYOffset / fPixelsPerSecond;
float fWidthEffect = pPlayerState->m_EffectHistory.GetSample( fSecond );
if( fWidthEffect != 0 && FRAME_WIDTH_LOCK_EFFECTS_TO_OVERLAPPING )
{
// Don't display effect data that happened before this hold overlapped the top.
float fFromEndOfOverlapped = fOverlappedTime - fSecond;
float fTrailingPixels = FRAME_WIDTH_LOCK_EFFECTS_TWEEN_PIXELS;
float fTrailingSeconds = fTrailingPixels / fPixelsPerSecond;
float fScaleEffect = SCALE( fFromEndOfOverlapped, 0.0f, fTrailingSeconds, 0.0f, 1.0f );
CLAMP( fScaleEffect, 0.0f, 1.0f );
fWidthEffect *= fScaleEffect;
}
if( fWidthEffect > 0 )
fFrameWidthMultiplier *= SCALE( fWidthEffect, 0.0f, 1.0f, 1.0f, FRAME_WIDTH_EFFECTS_MAX_MULTIPLIER );
else if( fWidthEffect < 0 )
fFrameWidthMultiplier *= SCALE( fWidthEffect, 0.0f, -1.0f, 1.0f, FRAME_WIDTH_EFFECTS_MIN_MULTIPLIER );
return fFrameWidthMultiplier;
}
2014-06-28 01:26:57 -05:00
// To provide reasonable defaults to methods below.
ThemeMetric<float> FADE_BEFORE_TARGETS_PERCENT( "NoteField", "FadeBeforeTargetsPercent" );
ThemeMetric<float> DRAW_DISTANCE_BEFORE_TARGET_PIXELS( "Player", "DrawDistanceBeforeTargetsPixels" );
ThemeMetric<float> GRAY_ARROWS_Y_STANDARD( "Player", "ReceptorArrowsYStandard" );
ThemeMetric<float> GRAY_ARROWS_Y_REVERSE( "Player", "ReceptorArrowsYReverse" );
// lua start
#include "LuaBinding.h"
namespace
{
/* Update() need to be exposed to use ArrowEffects off ScreenGameplay. It is harmless. */
int Update( lua_State *L ) { ArrowEffects::Update(); return 0; }
// Provide a reasonable default value for fYReverseOffset
float YReverseOffset( lua_State *L, int argnum )
{
float fYReverseOffsetPixels = GRAY_ARROWS_Y_REVERSE - GRAY_ARROWS_Y_STANDARD;
if( lua_gettop(L) >= argnum && !lua_isnil(L, argnum) )
{
fYReverseOffsetPixels = FArg(argnum);
}
return fYReverseOffsetPixels;
}
// ( PlayerState ps, int iCol, float fNoteBeat )
int GetYOffset( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
2014-06-28 01:26:57 -05:00
float fPeakYOffset;
bool bIsPastPeak;
lua_pushnumber( L, ArrowEffects::GetYOffset( ps, IArg(2)-1, FArg(3), fPeakYOffset, bIsPastPeak ) );
lua_pushnumber( L, fPeakYOffset );
lua_pushboolean( L, bIsPastPeak );
return 3;
}
// ( PlayerState ps, int iCol, float fYOffset, float fYReverseOffsetPixels )
int GetYPos( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
float fYReverseOffsetPixels = YReverseOffset( L, 4 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
lua_pushnumber(L, ArrowEffects::GetYPos(ps, IArg(2)-1, FArg(3), fYReverseOffsetPixels));
2014-06-28 01:26:57 -05:00
return 1;
}
// ( PlayerState ps, int iCol, float fYPos, float fYReverseOffsetPixels )
int GetYOffsetFromYPos( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
2014-06-28 01:26:57 -05:00
float fYReverseOffsetPixels = YReverseOffset( L, 4 );
lua_pushnumber(L, ArrowEffects::GetYOffsetFromYPos(IArg(2)-1, FArg(3), fYReverseOffsetPixels));
2014-06-28 01:26:57 -05:00
return 1;
}
// ( PlayerState ps, int iCol, float fYOffset )
int GetXPos( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
2014-06-28 01:26:57 -05:00
lua_pushnumber( L, ArrowEffects::GetXPos( ps, IArg(2)-1, FArg(3) ) );
return 1;
}
// ( PlayerState ps, int iCol, float fYOffset )
int GetZPos( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
lua_pushnumber(L, ArrowEffects::GetZPos( ps, IArg(2)-1, FArg(3)));
2014-06-28 01:26:57 -05:00
return 1;
}
// ( PlayerState ps, float fYOffset, int iCol )
2014-06-28 01:26:57 -05:00
int GetRotationX( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
2017-02-08 05:44:33 -08:00
bool bIsHoldCap = false;
lua_pushnumber(L, ArrowEffects::GetRotationX(ps,FArg(2), bIsHoldCap, IArg(3)-1));
2014-06-28 01:26:57 -05:00
return 1;
}
// ( PlayerState ps, float fYOffset, int iCol )
2014-06-28 01:26:57 -05:00
int GetRotationY( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
lua_pushnumber(L, ArrowEffects::GetRotationY(ps, FArg(2), IArg(3)-1));
2014-06-28 01:26:57 -05:00
return 1;
}
// ( PlayerState ps, float fNoteBeat, bool bIsHoldHead, int iCol )
2014-06-28 01:26:57 -05:00
int GetRotationZ( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
2014-06-28 01:26:57 -05:00
// Make bIsHoldHead optional.
bool bIsHoldHead = false;
if( lua_gettop(L) >= 3 && !lua_isnil(L, 3) )
{
bIsHoldHead = BArg(3);
}
lua_pushnumber( L, ArrowEffects::GetRotationZ( ps, FArg(2), bIsHoldHead, IArg(4)-1 ) );
2014-06-28 01:26:57 -05:00
return 1;
}
// ( PlayerState ps, int iCol )
2014-06-28 01:26:57 -05:00
int ReceptorGetRotationZ( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
lua_pushnumber( L, ArrowEffects::ReceptorGetRotationZ( ps, IArg(2)-1 ) );
2014-06-28 01:26:57 -05:00
return 1;
}
//( PlayerState ps, int iCol, float fYOffset, float fPercentFadeToFail, float fYReverseOffsetPixels, float fDrawDistanceBeforeTargetsPixels, float fFadeInPercentOfDrawFar )
int GetAlpha( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
2014-06-28 01:26:57 -05:00
// Provide reasonable default values.
float fPercentFadeToFail = -1;
float fYReverseOffsetPixels = YReverseOffset( L, 5 );
float fDrawDistanceBeforeTargetsPixels = DRAW_DISTANCE_BEFORE_TARGET_PIXELS;
float fFadeInPercentOfDrawFar = FADE_BEFORE_TARGETS_PERCENT;
if( lua_gettop(L) >= 4 && !lua_isnil(L, 4) )
{
fPercentFadeToFail = FArg(4);
}
if( lua_gettop(L) >= 6 && !lua_isnil(L, 6) )
{
fDrawDistanceBeforeTargetsPixels = FArg(6);
}
if( lua_gettop(L) >= 7 && !lua_isnil(L, 7) )
{
fFadeInPercentOfDrawFar = FArg(7);
}
lua_pushnumber(L, ArrowEffects::GetAlpha(ps, IArg(2)-1, FArg(3), fPercentFadeToFail, fYReverseOffsetPixels, fDrawDistanceBeforeTargetsPixels, fFadeInPercentOfDrawFar));
2014-06-28 01:26:57 -05:00
return 1;
}
//( PlayerState ps, int iCol, float fYOffset, float fPercentFadeToFail, float fYReverseOffsetPixels, float fDrawDistanceBeforeTargetsPixels, float fFadeInPercentOfDrawFar )
int GetGlow( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
2014-06-28 01:26:57 -05:00
// Provide reasonable default values.
float fPercentFadeToFail = -1; //
float fYReverseOffsetPixels = YReverseOffset( L, 5 );
float fDrawDistanceBeforeTargetsPixels = DRAW_DISTANCE_BEFORE_TARGET_PIXELS;
float fFadeInPercentOfDrawFar = FADE_BEFORE_TARGETS_PERCENT;
if( lua_gettop(L) >= 4 && !lua_isnil(L, 4) )
{
fPercentFadeToFail = FArg(4);
}
if( lua_gettop(L) >= 6 && !lua_isnil(L, 6) )
{
fDrawDistanceBeforeTargetsPixels = FArg(6);
}
if( lua_gettop(L) >= 7 && !lua_isnil(L, 7) )
{
fFadeInPercentOfDrawFar = FArg(7);
}
lua_pushnumber( L, ArrowEffects::GetGlow(ps, IArg(2)-1, FArg(3), fPercentFadeToFail, fYReverseOffsetPixels, fDrawDistanceBeforeTargetsPixels, fFadeInPercentOfDrawFar ) );
2014-06-28 01:26:57 -05:00
return 1;
}
// ( PlayerState ps, float fNoteBeat )
int GetBrightness( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
2014-06-28 01:26:57 -05:00
lua_pushnumber( L, ArrowEffects::GetBrightness( ps, FArg(2) ) );
return 1;
}
// ( PlayerState ps )
int NeedZBuffer( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
lua_pushboolean(L, ArrowEffects::NeedZBuffer());
2014-06-28 01:26:57 -05:00
return 1;
}
// ( PlayerState ps, float fYOffset, int iCol )
2014-06-28 01:26:57 -05:00
int GetZoom( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
lua_pushnumber( L, ArrowEffects::GetZoom( ps, FArg(2), IArg(3)-1 ) );
2014-06-28 01:26:57 -05:00
return 1;
}
// ( PlayerState ps, float fYOffset, fOverlappedTime )
int GetFrameWidthScale( lua_State *L )
{
PlayerState *ps = Luna<PlayerState>::check( L, 1 );
ArrowEffects::SetCurrentOptions(&ps->m_PlayerOptions.GetCurrent());
2014-06-28 01:26:57 -05:00
// Make fOverlappedTime optional.
float fOverlappedTime = 0;
if( lua_gettop(L) >= 3 && !lua_isnil(L, 3) )
{
fOverlappedTime = FArg(3);
}
lua_pushnumber( L, ArrowEffects::GetFrameWidthScale( ps, FArg(2), fOverlappedTime ) );
return 1;
}
const luaL_Reg ArrowEffectsTable[] =
{
LIST_METHOD( Update ),
LIST_METHOD( GetYOffset ),
LIST_METHOD( GetYPos ),
LIST_METHOD( GetYOffsetFromYPos ),
LIST_METHOD( GetXPos ),
LIST_METHOD( GetZPos ),
LIST_METHOD( GetRotationX ),
LIST_METHOD( GetRotationY ),
LIST_METHOD( GetRotationZ ),
LIST_METHOD( ReceptorGetRotationZ ),
LIST_METHOD( GetAlpha ),
LIST_METHOD( GetGlow ),
LIST_METHOD( GetBrightness ),
LIST_METHOD( NeedZBuffer ),
LIST_METHOD( GetZoom ),
LIST_METHOD( GetFrameWidthScale ),
2019-06-22 12:35:38 -07:00
{ nullptr, nullptr }
2014-06-28 01:26:57 -05:00
};
}
LUA_REGISTER_NAMESPACE( ArrowEffects )
2011-03-17 01:47:30 -04:00
/*
* (c) 2001-2004 Chris Danford
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/