2023-10-10 18:10:21 +00:00
using System ;
2023-09-14 11:13:53 +00:00
using System.Collections.Generic ;
2023-10-05 09:29:43 +00:00
using System.Linq ;
2023-09-14 11:13:53 +00:00
using TMPro ;
using UnityEngine ;
public class TargetLevelProbabilityPanel : MonoBehaviour
{
2023-10-22 16:54:30 +00:00
[SerializeField] private GameObject singleLevelProbabilityPanel ;
[SerializeField] private GameObject targetTitleText ;
2023-09-14 11:13:53 +00:00
private GameObject titleText ;
2023-10-07 18:05:54 +00:00
public List < GameObject > singleLevelPanelsObjs = new List < GameObject > ( ) ;
public List < SingleLevelProbabilityPanel > singleLevelPanels = new List < SingleLevelProbabilityPanel > ( ) ;
2023-09-14 11:13:53 +00:00
2023-10-05 09:29:43 +00:00
private int panelNum = 0 ;
2023-09-14 11:13:53 +00:00
public void IntializePanels ( int levelNum , string titleName )
{
// initialize target level probability panel size
float defaultWidth = singleLevelProbabilityPanel . GetComponent < RectTransform > ( ) . sizeDelta . x ;
float defaultLevelHeight = singleLevelProbabilityPanel . GetComponent < RectTransform > ( ) . sizeDelta . y ;
float titleHeight = targetTitleText . GetComponent < RectTransform > ( ) . sizeDelta . y ;
2023-10-05 09:29:43 +00:00
float averageProbability = 1f / levelNum ;
float lastLevelProbability = 1f - averageProbability * ( levelNum - 1 ) ;
2023-10-07 18:05:54 +00:00
// Debug.Log("averageProbability: " + averageProbability);
2023-09-14 11:13:53 +00:00
transform . GetComponent < RectTransform > ( ) . sizeDelta = new Vector2 ( defaultWidth , ( defaultLevelHeight * levelNum ) + titleHeight ) ;
// create title text
titleText = Instantiate ( targetTitleText , transform ) ;
titleText . GetComponent < TextMeshProUGUI > ( ) . text = titleName ;
2023-10-05 09:29:43 +00:00
// create and initialize single level probability panels
2023-09-14 11:13:53 +00:00
for ( int i = 0 ; i < levelNum ; i + + )
{
2023-10-05 09:29:43 +00:00
int tempIndex = i ;
2023-09-14 11:13:53 +00:00
singleLevelPanelsObjs . Add ( Instantiate ( singleLevelProbabilityPanel , transform ) ) ;
singleLevelPanels . Add ( singleLevelPanelsObjs [ i ] . GetComponent < SingleLevelProbabilityPanel > ( ) ) ;
2023-10-05 09:29:43 +00:00
singleLevelPanels [ i ] . InitializeLevelProbabilityPanel ( i , i = = levelNum - 1 ? lastLevelProbability : averageProbability ) ;
//add onValueChanged event to slider and input field
2023-10-10 18:10:21 +00:00
singleLevelPanels [ i ] . probabilitySlider . onValueChanged . AddListener ( ( value ) = > OnProbabilityValueChange ( value , tempIndex ) ) ;
singleLevelPanels [ i ] . inputField . onEndEdit . AddListener ( ( value ) = > OnProbabilityValueChange ( value , tempIndex ) ) ;
2023-10-05 09:29:43 +00:00
}
panelNum = levelNum ;
}
2023-10-05 14:49:34 +00:00
/// <summary>
/// On Probability Slider Value Change.Adjust other sliders' value to make sure the total value is 1.
/// </summary>
/// <param name="value">change to this value</param>
/// <param name="exceptedIndex">changed panel index</param>
2023-10-10 18:10:21 +00:00
private void OnProbabilityValueChange < T > ( T value , int exceptedIndex )
2023-10-05 09:29:43 +00:00
{
2023-10-10 18:10:21 +00:00
float changedValue = 0f ;
switch ( value )
{
case float floatValue :
changedValue = floatValue ;
break ;
2023-10-22 16:54:30 +00:00
2023-10-10 18:10:21 +00:00
case string stringValue :
changedValue = float . Parse ( stringValue ) ;
// limit the value between 0 and 1
2023-10-22 16:54:30 +00:00
if ( changedValue > 1 & & changedValue < = 100 )
2023-10-10 18:10:21 +00:00
{
changedValue / = 100 ;
2023-10-22 16:54:30 +00:00
}
else if ( changedValue > 100 )
2023-10-10 18:10:21 +00:00
{
changedValue = 1 ;
}
break ;
2023-10-22 16:54:30 +00:00
2023-10-10 18:10:21 +00:00
default :
Debug . LogError ( "Invalid value type!" ) ;
throw new ArgumentException ( "Unsupported value type" ) ;
}
2023-10-07 18:05:54 +00:00
float newTotalValue = 0 ; // new total probability value without locked panel
2023-10-05 09:29:43 +00:00
int unlockedPanelNum = 0 ;
int remainCorrectionNum = panelNum ;
2023-10-07 18:05:54 +00:00
float extraValue = 0 ; // the extra value after probability value changed
float maxLimitValue = 1 ; // the max value of unlocked panel permitted
2023-10-05 09:29:43 +00:00
float [ ] correctionValues = new float [ panelNum ] ;
// calculate total probability value
for ( int i = 0 ; i < panelNum ; i + + )
{
// disable slider listener
singleLevelPanels [ i ] . probabilitySlider . onValueChanged . RemoveAllListeners ( ) ;
if ( singleLevelPanels [ i ] . UnLocked )
{
unlockedPanelNum + + ;
2023-10-10 18:10:21 +00:00
newTotalValue + = ( i = = exceptedIndex ? changedValue : singleLevelPanels [ i ] . ProbabilityValue ) ;
2023-10-07 18:05:54 +00:00
}
else
{
// delete locked probability value from maxLimitValue
maxLimitValue - = singleLevelPanels [ i ] . ProbabilityValue ;
2023-10-05 09:29:43 +00:00
}
}
// only have one panel
if ( panelNum = = 1 )
{
singleLevelPanels [ exceptedIndex ] . SetProbability ( 1 ) ;
enableSliderListener ( ) ;
return ;
}
//only one panel is unlocked
if ( unlockedPanelNum = = 1 )
{
2023-10-07 18:05:54 +00:00
// limit this panel value at maxLimitValue
singleLevelPanels [ exceptedIndex ] . SetProbability ( maxLimitValue ) ;
2023-10-05 09:29:43 +00:00
enableSliderListener ( ) ;
return ;
}
// Calculate the average correction value
2023-10-07 18:05:54 +00:00
extraValue = newTotalValue - maxLimitValue ;
2023-10-05 09:29:43 +00:00
// initialize correction value to each panel
2023-10-05 14:49:34 +00:00
correctionValues = Enumerable . Repeat ( extraValue / ( unlockedPanelNum - 1 ) , panelNum ) . ToArray ( ) ;
2023-10-05 09:29:43 +00:00
// make sure all probability value is not less than 0 and equal to 1
int iterationCount = 0 ;
while ( remainCorrectionNum > 0 )
{
iterationCount + + ;
2023-10-10 18:10:21 +00:00
( correctionValues , remainCorrectionNum ) = reCalculateCorrectionValues ( correctionValues , exceptedIndex , changedValue , extraValue , maxLimitValue ) ;
2023-10-05 13:56:09 +00:00
// protect the infinite loop
2023-10-05 09:29:43 +00:00
if ( iterationCount > = 100 )
{
2023-10-05 13:56:09 +00:00
Debug . LogError ( "Infinite loop detected!" ) ;
2023-10-05 09:29:43 +00:00
break ;
}
}
// applicate the value to all unlocked panels excepted the changed one
2023-10-05 13:56:09 +00:00
applyCorrectionValue ( correctionValues ) ;
2023-10-05 09:29:43 +00:00
enableSliderListener ( ) ;
}
2023-10-05 14:49:34 +00:00
/// <summary>
/// enable all sliders' onValueChanged action listener
/// </summary>
2023-10-05 09:29:43 +00:00
private void enableSliderListener ( )
{
for ( int i = 0 ; i < panelNum ; i + + )
{
int tempIndex = i ;
2023-10-10 18:10:21 +00:00
singleLevelPanels [ i ] . probabilitySlider . onValueChanged . AddListener ( ( value ) = > OnProbabilityValueChange ( value , tempIndex ) ) ;
2023-09-14 11:13:53 +00:00
}
}
2023-10-05 13:56:09 +00:00
2023-10-05 14:49:34 +00:00
/// <summary>
2023-10-22 16:54:30 +00:00
/// Recalculates correction values.
2023-10-05 14:49:34 +00:00
/// </summary>
2023-10-22 16:54:30 +00:00
/// <param name="correctionValues">The current array of correction values.</param>
/// <param name="exceptedIndex">The index of the panel that is expected not to change.</param>
/// <param name="value">The expected probability value.</param>
/// <param name="extraValue">Additional correction value.</param>
/// <param name="maxLimitValue">The maximum limit for the probability value.</param>
/// <returns>Returns a tuple containing a float array and an integer.
/// The float array is the new correction values, and the integer is the number of panels that need to be corrected in the next iteration.</returns>
/// <remarks>
/// This method calculates new correction values based on the provided parameters.
/// During the iteration, some panels might exceed set limits, and their values will need to be corrected in the next iteration.
/// </remarks>
2023-10-07 18:05:54 +00:00
private ( float [ ] , int ) reCalculateCorrectionValues ( float [ ] correctionValues , int exceptedIndex , float value , float extraValue , float maxLimitValue )
2023-10-05 13:56:09 +00:00
{
2023-10-07 18:05:54 +00:00
// the number of panels which need to be corrected in next iteration
2023-10-05 13:56:09 +00:00
int remainCorrectionNum = 0 ;
List < int > reCorrectionIndex = new List < int > ( ) ;
2023-10-07 18:05:54 +00:00
// the index of the last panel which need to be corrected in this iteration
2023-10-05 13:56:09 +00:00
int lastReCorrectionIndex = 0 ;
2023-10-07 18:05:54 +00:00
// another extra value after correctionValue applicate to probability value which value also less than 0
// this value will be added to the panel which value is bigger.
2023-10-05 13:56:09 +00:00
float underZeroExtraValueAfterCorrected = 0 ;
for ( int i = 0 ; i < panelNum ; + + i )
{
// if the panel is the changed one
if ( i = = exceptedIndex )
{
2023-10-07 18:05:54 +00:00
// limit the value under maxLimitValue
correctionValues [ i ] = value > maxLimitValue ? ( singleLevelPanels [ i ] . ProbabilityValue - maxLimitValue ) : ( singleLevelPanels [ i ] . ProbabilityValue - value ) ;
2023-10-05 13:56:09 +00:00
continue ;
}
2023-10-05 14:49:34 +00:00
// if the panel is locked
if ( singleLevelPanels [ i ] . IsLocked )
{
correctionValues [ i ] = 0 ;
continue ;
}
2023-10-07 18:05:54 +00:00
// if the panel is unlocked
// and the probability value is less than 0 or bigger than maxLimitValue
if ( singleLevelPanels [ i ] . ProbabilityValue < 0 | | singleLevelPanels [ i ] . ProbabilityValue > maxLimitValue )
2023-10-05 13:56:09 +00:00
{
2023-10-07 18:05:54 +00:00
// correct this panel value to 0 or maxLimitValue
correctionValues [ i ] = singleLevelPanels [ i ] . ProbabilityValue ;
2023-10-05 13:56:09 +00:00
}
2023-10-07 18:05:54 +00:00
// probability value will be correct to 0
2023-10-05 13:56:09 +00:00
else if ( singleLevelPanels [ i ] . ProbabilityValue = = correctionValues [ i ] )
{
2023-10-07 18:05:54 +00:00
// that's good keep this correction value, and add it to the extraValue
2023-10-05 13:56:09 +00:00
underZeroExtraValueAfterCorrected + = correctionValues [ i ] ;
}
2023-10-07 18:05:54 +00:00
// probability value will be correct over the limit (bigger than maxLimitValue or less than 0)
else if ( singleLevelPanels [ i ] . ProbabilityValue - correctionValues [ i ] < 0 | | singleLevelPanels [ i ] . ProbabilityValue - correctionValues [ i ] > maxLimitValue )
2023-10-05 13:56:09 +00:00
{
2023-10-07 18:05:54 +00:00
// should be corrected to 0 or maxLimitValue,and add it to the extraValue
underZeroExtraValueAfterCorrected + = singleLevelPanels [ i ] . ProbabilityValue ;
2023-10-05 13:56:09 +00:00
correctionValues [ i ] = singleLevelPanels [ i ] . ProbabilityValue ;
}
2023-10-07 18:05:54 +00:00
// left panels correction value which need to be re-corrected
2023-10-05 13:56:09 +00:00
else
{
lastReCorrectionIndex = i ;
reCorrectionIndex . Add ( i ) ;
}
}
foreach ( int index in reCorrectionIndex )
{
2023-10-07 18:05:54 +00:00
// calculate the average correction value
2023-10-05 13:56:09 +00:00
float newAverageCorrectionValue = ( extraValue - underZeroExtraValueAfterCorrected ) / reCorrectionIndex . Count ;
2023-10-07 18:05:54 +00:00
// if the panel is the last one which need to be corrected.
// Avoidance of floating-point rounding error, the last panel correction value will be the extraValue minus the sum of the other panels correction value
2023-10-05 13:56:09 +00:00
if ( index = = lastReCorrectionIndex )
{
correctionValues [ index ] = extraValue - ( newAverageCorrectionValue * ( reCorrectionIndex . Count - 1 ) ) ;
2023-10-07 18:05:54 +00:00
// still out of limit after correction, correct it to 0 and activate next iteration
if ( singleLevelPanels [ index ] . ProbabilityValue - correctionValues [ index ] < 0 | | singleLevelPanels [ index ] . ProbabilityValue - correctionValues [ index ] > maxLimitValue )
2023-10-05 13:56:09 +00:00
{
correctionValues [ index ] = singleLevelPanels [ index ] . ProbabilityValue ;
remainCorrectionNum + + ;
}
}
else
{
correctionValues [ index ] = newAverageCorrectionValue ;
2023-10-07 18:05:54 +00:00
// still out of limit after correction, correct it to 0 and activate next iteration
if ( singleLevelPanels [ index ] . ProbabilityValue - correctionValues [ index ] < 0 | | singleLevelPanels [ index ] . ProbabilityValue - correctionValues [ index ] > maxLimitValue )
2023-10-05 13:56:09 +00:00
{
correctionValues [ index ] = singleLevelPanels [ index ] . ProbabilityValue ;
remainCorrectionNum + + ;
}
}
}
return ( correctionValues , remainCorrectionNum ) ;
}
2023-10-05 14:49:34 +00:00
/// <summary>
/// applicate correction value to each panel
/// </summary>
2023-10-05 13:56:09 +00:00
private void applyCorrectionValue ( float [ ] correctionValues )
{
for ( int i = 0 ; i < panelNum ; i + + )
{
2023-10-07 18:05:54 +00:00
/ * if ( singleLevelPanels [ i ] . ProbabilityValue - correctionValues [ i ] < 0 )
{
Debug . LogWarning ( "Probability value is less than 0" ) ;
Debug . Log ( i ) ;
Debug . Log ( singleLevelPanels [ i ] . ProbabilityValue ) ;
Debug . Log ( correctionValues [ i ] ) ;
} * /
2023-10-05 13:56:09 +00:00
singleLevelPanels [ i ] . SetProbability ( singleLevelPanels [ i ] . ProbabilityValue - correctionValues [ i ] ) ;
}
}
2023-09-14 11:13:53 +00:00
}