Merge branch 'ItemWeights' of git://github.com/STK-helper/stk-code into STK-helper-ItemWeights
A very rough merge to solve conflicts, not expected to even work.
This commit is contained in:
commit
ea229b4e5e
139
data/powerup.xml
139
data/powerup.xml
@ -74,38 +74,131 @@
|
||||
force-updown="35" force-to-target="15"
|
||||
max-distance="25" />
|
||||
|
||||
<!-- Distribution of the items depending on position of the
|
||||
kart (first kart, top 33%, middle 33%, bottom 33% (but
|
||||
not last), last kart. i
|
||||
<!-- This defines the number of karts for which there is a
|
||||
defined weight list. The value between two nodes will be
|
||||
interpolated as a linear average of the value at each node,
|
||||
except under the minimum node and above the maximum node
|
||||
(in which case it stays identical to the minimum/maximum node)
|
||||
|
||||
The num of karts associated with each reference point should be
|
||||
sorted in ascending order to guarantee correct behavior.
|
||||
|
||||
This doesn't apply to follow the leader, battle & tutorial weights
|
||||
If the max karts number is increased, think to add values for
|
||||
higher kart numbers. Otherwhise, it will default to value for 20. -->
|
||||
<reference_points ref1="1" ref2="5" ref3="9" ref4="14" ref5="20" />
|
||||
|
||||
<!-- This defines the probabilities to get each type of item depending on
|
||||
the position of the kart.
|
||||
There are defined value for first kart, last kart, and
|
||||
at three equally spaced points between those.
|
||||
For example, with 5 karts those points will match positions 2, 3, 4.
|
||||
With 10 karts, they will correspond to 3,25 ; 5,5 ; 7,75.
|
||||
It is not an issue for it to be a float, as the weights for positions
|
||||
are linearly averaged.
|
||||
The order of items must correspond to powerup_manager.hpp.
|
||||
The first line (w=...) corresponds to the weights of
|
||||
getting a single item, the second line (w-multi) to
|
||||
getting three identical items instead of just a single one.
|
||||
E.g. as the first kart, you have 3/8 of a chance to get
|
||||
a single bubble gum, 1/8 to get a bowling ball or switch,
|
||||
and 3/8 to get a triple bubble gum.
|
||||
'Global' items which affect all karts (switch) should
|
||||
|
||||
The first line (w1=...) corresponds to the weights of
|
||||
getting a specific item, the second line (w-multi1) to
|
||||
the weight at which it will yeld a triple item
|
||||
rather than a single one
|
||||
|
||||
w1 corresponds to the weights at ref1, w2 to the weights at ref2, etc.
|
||||
When the number of karts in the race fall between refi and refj,
|
||||
the weights used are a weighted average between wi and wj.
|
||||
|
||||
wf and w-multif are the weights associated with the follow-the-leader
|
||||
mode. Those are the same for all kart numbers.
|
||||
|
||||
The probability to get an item is its weight divided by
|
||||
sum of weights of all items (single AND multi). It is recommended
|
||||
to keep that sum equal to 200 to easily keep track of probabilities.
|
||||
|
||||
'Global' items which affect all karts (switch, parachute) should
|
||||
be quite rare, since otherwise the item might be used
|
||||
too often (compared with many items which will only
|
||||
affect a karts or two). -->
|
||||
affect a karts or two).
|
||||
|
||||
The distribution should give more similar items to different ranks
|
||||
when there are few karts, but have more important differences when
|
||||
there are more karts. -->
|
||||
<!-- bubble cake bowl zipper plunger switch swattr rubber para anvil -->
|
||||
<first w1="28 0 60 20 45 15 32 0 0 0"
|
||||
w-multi1=" 0 0 0 0 0 0 0 0 0 0"
|
||||
w2="26 16 52 15 46 12 33 0 0 0"
|
||||
w-multi2=" 0 0 0 0 0 0 0 0 0 0"
|
||||
w3="24 12 58 10 54 8 34 0 0 0"
|
||||
w-multi3=" 0 0 0 0 0 0 0 0 0 0"
|
||||
w4="22 8 64 5 60 6 35 0 0 0"
|
||||
w-multi4=" 0 0 0 0 0 0 0 0 0 0"
|
||||
w5="20 0 74 0 66 4 36 0 0 0"
|
||||
w-multi5=" 0 0 0 0 0 0 0 0 0 0"
|
||||
wf="35 0 25 35 25 15 25 0 0 0"
|
||||
w-multif="20 0 0 20 0 0 0 0 0 0" />
|
||||
|
||||
<!-- bubble cake bowl zipper plunger switch swattr rubber para anvil -->
|
||||
<top33 w1="30 0 59 26 40 14 27 0 0 0"
|
||||
w-multi1=" 0 0 4 0 0 0 0 0 0 0"
|
||||
w2="30 27 48 25 28 10 27 0 0 0"
|
||||
w-multi2=" 0 0 5 0 0 0 0 0 0 0"
|
||||
w3="29 30 45 24 32 7 27 0 0 0"
|
||||
w-multi3=" 0 0 6 0 0 0 0 0 0 0"
|
||||
w4="28 31 48 22 34 4 27 0 0 0"
|
||||
w-multi4=" 0 0 6 0 0 0 0 0 0 0"
|
||||
w5="27 32 48 20 37 3 27 0 0 0"
|
||||
w-multi5=" 0 0 6 0 0 0 0 0 0 0"
|
||||
wf="25 0 60 25 58 2 30 0 0 0"
|
||||
w-multif=" 0 0 0 0 0 0 0 0 0 0" />
|
||||
|
||||
<!-- bubble cake bowl zipper plunger switch swattr rubber para anvil -->
|
||||
<mid33 w1="30 0 62 27 36 13 27 0 0 0"
|
||||
w-multi1=" 0 0 5 0 0 0 0 0 0 0"
|
||||
w2="30 27 45 27 27 9 27 3 0 0"
|
||||
w-multi2=" 0 0 5 0 0 0 0 0 0 0"
|
||||
w3="30 26 41 28 26 6 26 10 0 0"
|
||||
w-multi3=" 0 0 7 0 0 0 0 0 0 0"
|
||||
w4="30 25 42 29 29 3 24 10 0 0"
|
||||
w-multi4=" 0 0 8 0 0 0 0 0 0 0"
|
||||
w5="30 24 40 30 28 2 21 10 0 0"
|
||||
w-multi5=" 0 0 10 0 5 0 0 0 0 0"
|
||||
wf="35 0 55 35 25 3 25 0 0 0"
|
||||
w-multif=" 0 0 10 0 12 0 0 0 0 0" />
|
||||
|
||||
<!-- bubble cake bowl zipper plunger switch swattr rubber para anvil -->
|
||||
<end33 w1="31 0 56 36 34 12 25 0 0 0"
|
||||
w-multi1=" 0 0 6 0 0 0 0 0 0 0"
|
||||
w2="32 24 28 38 22 7 20 16 6 0"
|
||||
w-multi2=" 0 0 7 0 0 0 0 0 0 0"
|
||||
w3="33 23 26 45 14 5 16 12 8 0"
|
||||
w-multi3=" 0 0 12 0 6 0 0 0 0 0"
|
||||
w4="27 21 23 44 12 3 14 8 6 0"
|
||||
w-multi4=" 8 0 16 8 10 0 0 0 0 0"
|
||||
w5="25 18 20 50 10 2 10 6 3 0"
|
||||
w-multi5="10 0 20 10 16 0 0 0 0 0"
|
||||
wf="25 0 40 45 15 5 15 10 5 0"
|
||||
w-multif="10 0 15 15 0 0 0 0 0 0" />
|
||||
|
||||
<!-- bubble cake bowl zipper plunger switch swattr rubber para anvil -->
|
||||
<last w1="34 0 36 55 30 10 17 0 0 0"
|
||||
w-multi1=" 0 0 18 0 0 0 0 0 0 0"
|
||||
w2="28 21 9 45 0 6 0 10 18 0"
|
||||
w-multi2=" 8 0 16 35 4 0 0 0 0 0"
|
||||
w3="20 16 7 37 0 3 0 4 15 0"
|
||||
w-multi3="18 0 18 58 4 0 0 0 0 0"
|
||||
w4="18 14 3 35 0 0 0 0 10 0"
|
||||
w-multi4="24 0 25 65 6 0 0 0 0 0"
|
||||
w5="15 12 0 25 0 0 0 0 7 0"
|
||||
w-multi5="30 0 31 80 0 0 0 0 0 0"
|
||||
wf="20 0 15 25 0 0 0 0 15 0"
|
||||
w-multif="20 0 25 80 0 0 0 0 0 0" />
|
||||
|
||||
<!-- bubble cake bowl zipper plunger switch swattr rubber para anvil -->
|
||||
<first w="25 5 15 5 10 10 10 0 0 0"
|
||||
w-multi=" 0 0 5 0 0 0 0 0 0 0" />
|
||||
<top33 w="30 30 30 30 30 10 10 10 10 0"
|
||||
w-multi=" 0 10 10 0 10 0 0 0 0 0" />
|
||||
<mid33 w="30 30 30 30 30 10 20 20 20 0"
|
||||
w-multi=" 0 20 20 20 20 0 0 0 0 0" />
|
||||
<end33 w=" 0 30 30 30 30 10 20 30 30 0"
|
||||
w-multi=" 0 30 30 30 30 0 0 0 0 0" />
|
||||
<last w=" 0 30 10 90 60 0 10 60 60 0"
|
||||
w-multi=" 0 30 30 100 60 0 0 0 0 0" />
|
||||
<battle w="10 30 60 0 0 10 30 0 0 0"
|
||||
w-multi=" 0 0 5 0 0 0 0 0 0 0" />
|
||||
<soccer w=" 0 30 60 0 0 10 30 0 0 0"
|
||||
w-multi=" 0 0 5 0 0 0 0 0 0 0" />
|
||||
<tuto w=" 0 0 0 0 0 0 0 0 0 0"
|
||||
w-multi=" 0 0 100 0 0 0 0 0 0 0" />
|
||||
w-multi=" 0 0 100 0 0 0 0 0 0 0" />
|
||||
|
||||
|
||||
</powerup>
|
||||
|
||||
|
@ -961,7 +961,7 @@ void IrrDriver::applyResolutionSettings()
|
||||
material_manager->addSharedMaterial(materials_file);
|
||||
}
|
||||
|
||||
powerup_manager->loadAllPowerups ();
|
||||
powerup_manager->loadPowerupsModels();
|
||||
ItemManager::loadDefaultItemMeshes();
|
||||
projectile_manager->loadData();
|
||||
Referee::init();
|
||||
|
409
src/items/powerup_manager.cpp
Normal file → Executable file
409
src/items/powerup_manager.cpp
Normal file → Executable file
@ -108,9 +108,9 @@ PowerupManager::PowerupType
|
||||
} // getPowerupType
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Loads all powerups from the powerup.xml file.
|
||||
/** Loads powerups models and icons from the powerup.xml file.
|
||||
*/
|
||||
void PowerupManager::loadAllPowerups()
|
||||
void PowerupManager::loadPowerupsModels()
|
||||
{
|
||||
const std::string file_name = file_manager->getAsset("powerup.xml");
|
||||
XMLNode *root = file_manager->createXMLTree(file_name);
|
||||
@ -132,14 +132,28 @@ void PowerupManager::loadAllPowerups()
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
loadWeights(*root, "first", POSITION_FIRST );
|
||||
loadWeights(*root, "top33", POSITION_TOP33 );
|
||||
loadWeights(*root, "mid33", POSITION_MID33 );
|
||||
loadWeights(*root, "end33", POSITION_END33 );
|
||||
loadWeights(*root, "last" , POSITION_LAST );
|
||||
loadWeights(*root, "battle" , POSITION_BATTLE_MODE);
|
||||
loadWeights(*root, "soccer" , POSITION_SOCCER_MODE);
|
||||
loadWeights(*root, "tuto", POSITION_TUTORIAL_MODE);
|
||||
delete root;
|
||||
} // loadPowerupsModels
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Loads all powerups weights from the powerup.xml file.
|
||||
* Uses num_karts to get the good weights
|
||||
*/
|
||||
void PowerupManager::loadAllPowerups(unsigned int num_karts)
|
||||
{
|
||||
const std::string file_name = file_manager->getAsset("powerup.xml");
|
||||
XMLNode *root = file_manager->createXMLTree(file_name);
|
||||
|
||||
//loadWeights takes care of loading specific weight in follow-the-leader
|
||||
//They also vary depending on rank in race, so they use the usual position classes
|
||||
loadWeights(*root, num_karts, "first", POSITION_FIRST );
|
||||
loadWeights(*root, num_karts, "top33", POSITION_TOP33 );
|
||||
loadWeights(*root, num_karts, "mid33", POSITION_MID33 );
|
||||
loadWeights(*root, num_karts, "end33", POSITION_END33 );
|
||||
loadWeights(*root, num_karts, "last" , POSITION_LAST );
|
||||
loadWeights(*root, num_karts, "battle" , POSITION_BATTLE_MODE);
|
||||
loadWeights(*root, num_karts, "soccer" , POSITION_SOCCER_MODE);
|
||||
loadWeights(*root, num_karts, "tuto", POSITION_TUTORIAL_MODE);
|
||||
|
||||
delete root;
|
||||
|
||||
@ -208,22 +222,113 @@ void PowerupManager::LoadPowerup(PowerupType type, const XMLNode &node)
|
||||
} // LoadNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Loads a weight list specified in powerup.xml. The different position
|
||||
* classes must be loaded in the right order
|
||||
/** Loads a weight list specified in powerup.xml.
|
||||
* Calculates a linear average of the weight specified for the reference points
|
||||
* of num_karts higher and lower (unless it exactly matches one)
|
||||
* \param root The root node of powerup.xml
|
||||
* \param num_karts The number of karts in the race
|
||||
* \param class_name The name of the position class to load.
|
||||
* \param position_class The class for which the weights are read.
|
||||
*/
|
||||
void PowerupManager::loadWeights(const XMLNode &root,
|
||||
unsigned int num_karts,
|
||||
const std::string &class_name,
|
||||
PositionClass position_class)
|
||||
{
|
||||
const XMLNode *node = root.getNode(class_name);
|
||||
std::string s(""), s_multi("");
|
||||
if(node) node->get("w", &s );
|
||||
if(node) node->get("w-multi", &s_multi);
|
||||
const XMLNode *class_node = root.getNode(class_name), *reference_points_node = root.getNode("reference_points");
|
||||
std::string ref;
|
||||
// Those are the strings which will contain the names of the nodes to load inside the class node
|
||||
std::string w_inf("w"), w_sup("w"), w_inf_multi("w-multi"), w_sup_multi("w-multi");
|
||||
// Those are the strings where the raw weight values will be put when read from the XML nodes
|
||||
std::string values_inf(""), values_inf_multi(""), values_sup(""), values_sup_multi("");
|
||||
int inf_num = 1, sup_num = 1; //The number of karts associated with the reference points
|
||||
|
||||
if(!node || s=="" || s_multi=="")
|
||||
/** Adds to w_inf and w_sup the suffixe of the closest num_karts reference classes
|
||||
*/
|
||||
if(position_class!=POSITION_BATTLE_MODE &&
|
||||
position_class!=POSITION_TUTORIAL_MODE &&
|
||||
position_class!=POSITION_SOCCER_MODE)
|
||||
{
|
||||
//First step : get the reference points
|
||||
|
||||
if(!reference_points_node)
|
||||
{
|
||||
Log::error("[PowerupManager]","No reference points found, "
|
||||
"probabilities will be incorrect");
|
||||
}
|
||||
|
||||
if (reference_points_node)
|
||||
{
|
||||
if(race_manager->isFollowMode())
|
||||
{
|
||||
w_inf = w_sup = "wf";
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
int i = 0;
|
||||
while(1)
|
||||
{
|
||||
i++;
|
||||
ref = "ref" + StringUtils::toString(i);
|
||||
std::string ref_pos;
|
||||
if (!reference_points_node->get(ref, &ref_pos))
|
||||
break;
|
||||
unsigned int ref_value;
|
||||
if (!StringUtils::parseString(ref_pos.c_str(), &ref_value))
|
||||
{
|
||||
Log::error("[PowerupManager]","Incorrect reference point values, "
|
||||
"probabilities will be incorrect");
|
||||
continue;
|
||||
}
|
||||
if (ref_value <= num_karts)
|
||||
{
|
||||
inf_num = ref_value;
|
||||
//Useful if config is edited so num_kart > highest reference position
|
||||
sup_num = ref_value;
|
||||
w_inf = "w" + StringUtils::toString(i);
|
||||
w_inf_multi = "w-multi" + StringUtils::toString(i);
|
||||
}
|
||||
//No else if, it can be equal
|
||||
if (ref_value >= num_karts)
|
||||
{
|
||||
sup_num = ref_value;
|
||||
w_sup = w_sup + StringUtils::toString(i);
|
||||
w_sup_multi = w_sup_multi + StringUtils::toString(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} //else
|
||||
}
|
||||
|
||||
if(w_inf=="w" || w_sup=="w")
|
||||
{
|
||||
w_inf = "w3"; //fallback values
|
||||
w_inf_multi = "w-multi3";//fallback values
|
||||
w_sup = "w3"; //fallback values
|
||||
w_sup_multi = "w-multi3";//fallback values
|
||||
inf_num = sup_num = num_karts;//fallback values
|
||||
Log::error("[PowerupManager]","Uncorrect reference points in powerup.xml."
|
||||
"%d karts - fallback probabilities will be used.",
|
||||
num_karts);
|
||||
}
|
||||
|
||||
// Now w_inf and w_sup contain the weight classes to use
|
||||
// for the weights calculations
|
||||
|
||||
}
|
||||
|
||||
//Second step : load the class_node values
|
||||
if(class_node)
|
||||
{
|
||||
class_node->get(w_inf, &values_inf );
|
||||
class_node->get(w_inf_multi, &values_inf_multi );
|
||||
class_node->get(w_sup, &values_sup );
|
||||
class_node->get(w_sup_multi, &values_sup_multi );
|
||||
}
|
||||
|
||||
if(!class_node || values_inf=="" || values_inf_multi==""
|
||||
|| values_sup=="" || values_sup_multi=="")
|
||||
{
|
||||
Log::error("[PowerupManager]", "No weights found for class '%s'"
|
||||
" - probabilities will be incorrect.",
|
||||
@ -231,91 +336,166 @@ void PowerupManager::loadWeights(const XMLNode &root,
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> weight_list = StringUtils::split(s+" "+s_multi,' ');
|
||||
//Third step : split in discrete values
|
||||
std::vector<std::string> weight_list_inf = StringUtils::split(values_inf+" "+values_inf_multi,' ');
|
||||
std::vector<std::string> weight_list_sup = StringUtils::split(values_sup+" "+values_sup_multi,' ');
|
||||
|
||||
std::vector<std::string>::iterator i=weight_list.begin();
|
||||
while(i!=weight_list.end())
|
||||
std::vector<std::string>::iterator i=weight_list_inf.begin();
|
||||
while(i!=weight_list_inf.end())
|
||||
{
|
||||
if(*i=="")
|
||||
{
|
||||
std::vector<std::string>::iterator next=weight_list.erase(i);
|
||||
std::vector<std::string>::iterator next=weight_list_inf.erase(i);
|
||||
i=next;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
// Fill missing entries with 0s
|
||||
while(weight_list.size()<2*(int)POWERUP_LAST)
|
||||
weight_list.push_back(0);
|
||||
|
||||
if(weight_list.size()!=2*(int)POWERUP_LAST)
|
||||
i=weight_list_sup.begin();
|
||||
while(i!=weight_list_sup.end())
|
||||
{
|
||||
Log::error("PowerupManager",
|
||||
"Incorrect number of weights found in class '%s':",
|
||||
class_name.c_str());
|
||||
Log::error("PowerupManager",
|
||||
"%d instead of %d - probabilities will be incorrect.",
|
||||
(int)weight_list.size(), (int)POWERUP_LAST);
|
||||
if(*i=="")
|
||||
{
|
||||
std::vector<std::string>::iterator next=weight_list_sup.erase(i);
|
||||
i=next;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
|
||||
// Fill missing entries with 0s
|
||||
while(weight_list_inf.size()<2*(int)POWERUP_LAST)
|
||||
weight_list_inf.push_back(0);
|
||||
|
||||
while(weight_list_sup.size()<2*(int)POWERUP_LAST)
|
||||
weight_list_sup.push_back(0);
|
||||
|
||||
//This also tests that the two lists are of equal size
|
||||
if(weight_list_inf.size()!=2*(int)POWERUP_LAST ||
|
||||
weight_list_sup.size()!=2*(int)POWERUP_LAST)
|
||||
{
|
||||
Log::error("[PowerupManager]", "Incorrect number of weights found in class '%s':",
|
||||
class_name.c_str());
|
||||
Log::error("[PowerupManager]", "%d and %d instead of twice %d - probabilities will be incorrect.",
|
||||
(int)weight_list_inf.size(), (int)weight_list_sup.size(), (int)POWERUP_LAST);
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int i=0; i<weight_list.size(); i++)
|
||||
//Fourth step : finally compute the searched values
|
||||
for(unsigned int i=0; i<2*(int)POWERUP_LAST; i++)
|
||||
{
|
||||
int w = atoi(weight_list[i].c_str());
|
||||
m_weights[position_class].push_back(w);
|
||||
}
|
||||
int weight_inf, weight_sup;
|
||||
|
||||
if (!StringUtils::parseString(weight_list_inf[i].c_str(), &weight_inf))
|
||||
{
|
||||
Log::error("[PowerupManager]","Incorrect powerup weight values, "
|
||||
"probabilities will be incorrect");
|
||||
weight_inf = 0;//default to zero
|
||||
}
|
||||
|
||||
if (!StringUtils::parseString(weight_list_sup[i].c_str(), &weight_sup))
|
||||
{
|
||||
Log::error("[PowerupManager]","Incorrect powerup weight values, "
|
||||
"probabilities will be incorrect");
|
||||
weight_sup = 0;
|
||||
}
|
||||
|
||||
//Do a linear average of the inf and sup values
|
||||
//Use float calculations to reduce the rounding errors
|
||||
float ref_diff = (float) (sup_num - inf_num);
|
||||
float sup_diff = (float) (sup_num - num_karts);
|
||||
float inf_diff = (float) (num_karts - inf_num);
|
||||
float weight;
|
||||
|
||||
if (ref_diff != 0)
|
||||
weight = (sup_diff/ref_diff)*weight_inf
|
||||
+(inf_diff/ref_diff)*weight_sup;
|
||||
|
||||
//the sup and inf weights are the same here, take one of them
|
||||
else
|
||||
weight = weight_inf;
|
||||
int rounded_weight = (int) weight;
|
||||
|
||||
m_weights[position_class].push_back(rounded_weight);
|
||||
}
|
||||
} // loadWeights
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** This function set up various arrays for faster lookup later. It first
|
||||
* determines which race position correspond to which position class, and
|
||||
* then filters which powerups are available (not all powerups might be
|
||||
* available in all races). It sets up two arrays: m_position_to_class
|
||||
* available in all races). It sets up two arrays: m_position_to_class_[inf/sup]
|
||||
* which maps which race position corresponds to which position class
|
||||
* \param num_kart Number of karts.
|
||||
*/
|
||||
void PowerupManager::updateWeightsForRace(unsigned int num_karts)
|
||||
{
|
||||
m_position_to_class.clear();
|
||||
if (num_karts == 0)
|
||||
return;
|
||||
|
||||
//This loads the appropriate weights
|
||||
loadAllPowerups(num_karts);
|
||||
|
||||
// In battle mode no positions exist, so use only position 1
|
||||
unsigned int end_position = (race_manager->isBattleMode() ||
|
||||
race_manager->isSoccerMode()) ? 1 : num_karts;
|
||||
|
||||
m_position_to_class_inf.clear();
|
||||
m_position_to_class_sup.clear();
|
||||
m_position_to_class_cutoff.clear();
|
||||
|
||||
for(unsigned int position =1; position <= end_position; position++)
|
||||
{
|
||||
// Set up the mapping of position to position class:
|
||||
// Set up the mapping of position to position class,
|
||||
// Using a linear average of the superior and inferor position class
|
||||
// -------------------------------------------------
|
||||
PositionClass pos_class = convertPositionToClass(num_karts, position);
|
||||
m_position_to_class.push_back(pos_class);
|
||||
|
||||
// Then determine which items are available. This loop actually goes
|
||||
// over the powerups twice: first the single item version, then the
|
||||
// multi-item version. The assignment to type takes care of this
|
||||
m_powerups_for_position[pos_class].clear();
|
||||
for(unsigned int i= POWERUP_FIRST; i<=2*POWERUP_LAST; i++)
|
||||
{
|
||||
PowerupType type =
|
||||
(PowerupType) ((i<=POWERUP_LAST) ? i
|
||||
: i+POWERUP_FIRST);
|
||||
unsigned int w =m_weights[pos_class][i-POWERUP_FIRST];
|
||||
// The 'global' powerups (i.e. powerups that affect
|
||||
// all karts, not only the ones close by) appear too
|
||||
// frequently with larger number of karts. To reduce
|
||||
// this effect their weight is reduced by the number
|
||||
// of karts.
|
||||
if(w!=0 && num_karts > 4 &&
|
||||
(type==POWERUP_PARACHUTE || type==POWERUP_SWITCH) )
|
||||
{
|
||||
w = w / (num_karts/4);
|
||||
if(w==0) w=1;
|
||||
}
|
||||
for(unsigned int j=0; j<w; j++)
|
||||
m_powerups_for_position[pos_class].push_back(type);
|
||||
} // for type in [POWERUP_FIRST, POWERUP_LAST]
|
||||
m_position_to_class_inf.push_back(convertPositionToClass(num_karts, position, false));
|
||||
m_position_to_class_sup.push_back(convertPositionToClass(num_karts, position, true));
|
||||
m_position_to_class_cutoff.push_back(convertPositionToClassWeight(num_karts, position));
|
||||
}
|
||||
|
||||
// Then determine which items are available.
|
||||
if (race_manager->isBattleMode())
|
||||
updatePowerupClass(POSITION_BATTLE_MODE);
|
||||
else if(race_manager->isSoccerMode())
|
||||
updatePowerupClass(POSITION_SOCCER_MODE);
|
||||
else if(race_manager->isTutorialMode())
|
||||
updatePowerupClass(POSITION_TUTORIAL_MODE);
|
||||
else
|
||||
{
|
||||
//TODO : replace this by a nice loop
|
||||
updatePowerupClass(POSITION_FIRST);
|
||||
updatePowerupClass(POSITION_TOP33);
|
||||
updatePowerupClass(POSITION_MID33);
|
||||
updatePowerupClass(POSITION_END33);
|
||||
updatePowerupClass(POSITION_LAST);
|
||||
}
|
||||
} // updateWeightsForRace
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** This function update the powerup array of a position class
|
||||
* \param pos_class The position class to update
|
||||
*/
|
||||
void PowerupManager::updatePowerupClass(PowerupManager::PositionClass pos_class)
|
||||
{
|
||||
|
||||
m_powerups_for_reference_pos[pos_class].clear();
|
||||
|
||||
// This loop actually goes over the powerups twice: first the single item version,
|
||||
// then the multi-item version. The assignment to type takes care of this
|
||||
for(unsigned int i= POWERUP_FIRST; i<=2*POWERUP_LAST; i++)
|
||||
{
|
||||
PowerupType type =
|
||||
(PowerupType) ((i<=POWERUP_LAST) ? i
|
||||
: i+POWERUP_FIRST);
|
||||
unsigned int w =m_weights[pos_class][i-POWERUP_FIRST];
|
||||
for(unsigned int j=0; j<w; j++)
|
||||
m_powerups_for_reference_pos[pos_class].push_back(type);
|
||||
} // for type in [POWERUP_FIRST, POWERUP_LAST]
|
||||
} //updatePowerupClass
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Determines the position class for a given position. If the race is a
|
||||
* battle mode (in which case we don't have a position), always return
|
||||
@ -323,10 +503,12 @@ void PowerupManager::updateWeightsForRace(unsigned int num_karts)
|
||||
* for all karts).
|
||||
* \param num_karts Number of karts in the race.
|
||||
* \param position The position for which to determine the position class.
|
||||
* \param class_sup true if it should send a class matching a >= position
|
||||
* false if it should match a <= position
|
||||
*/
|
||||
PowerupManager::PositionClass
|
||||
PowerupManager::convertPositionToClass(unsigned int num_karts,
|
||||
unsigned int position)
|
||||
unsigned int position, bool class_sup)
|
||||
{
|
||||
if(race_manager->isBattleMode()) return POSITION_BATTLE_MODE;
|
||||
if(race_manager->isSoccerMode()) return POSITION_SOCCER_MODE;
|
||||
@ -334,18 +516,82 @@ PowerupManager::PositionClass
|
||||
if(position==1) return POSITION_FIRST;
|
||||
if(position==num_karts) return POSITION_LAST;
|
||||
|
||||
// Now num_karts must be >2, since position <=num_players
|
||||
// Now num_karts must be >=3, since position <=num_players
|
||||
|
||||
unsigned int third = (unsigned int)floor((float)(num_karts-1)/3.0f);
|
||||
// 1 < Position <= 1+third is top33
|
||||
if(position <= 1 + third) return POSITION_TOP33;
|
||||
//Special case for Follow the Leader : top33 is mapped to the first non-leader kart
|
||||
if (race_manager->isFollowMode())
|
||||
{
|
||||
float third = (float) (num_karts-2)/3.0f;
|
||||
|
||||
// num_players-third < position is end33
|
||||
if(num_karts - third <= position) return POSITION_END33;
|
||||
if(position == 2) return POSITION_TOP33;
|
||||
|
||||
return POSITION_MID33;
|
||||
if (class_sup)
|
||||
{
|
||||
if(position <= 2 + third) return POSITION_MID33;
|
||||
else if(position <= 2 + 2*third) return POSITION_END33;
|
||||
else return POSITION_LAST;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(position >= 2 + 2*third) return POSITION_END33;
|
||||
else if(position >= 2 + third) return POSITION_MID33;
|
||||
else return POSITION_TOP33;
|
||||
}
|
||||
}
|
||||
|
||||
//Three points divide a line in 4 sections
|
||||
float quarter = (float) (num_karts-1)/4.0f;
|
||||
if (class_sup)
|
||||
{
|
||||
if(position <= 1 + quarter) return POSITION_TOP33;
|
||||
else if(position <= 1 + 2*quarter) return POSITION_MID33;
|
||||
else if(position <= 1 + 3*quarter) return POSITION_END33;
|
||||
else return POSITION_LAST;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(position >= 1 + 3*quarter) return POSITION_END33;
|
||||
else if(position >= 1 + 2*quarter) return POSITION_MID33;
|
||||
else if(position >= 1 + 1*quarter) return POSITION_TOP33;
|
||||
else return POSITION_FIRST;
|
||||
}
|
||||
} // convertPositionToClass
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Computes the proportion of the inf position class for a given position.
|
||||
* \param num_karts Number of karts in the race.
|
||||
* \param position The position for which to determine the proportion.
|
||||
*/
|
||||
unsigned int PowerupManager::convertPositionToClassWeight(unsigned int num_karts,
|
||||
unsigned int position)
|
||||
{
|
||||
if(race_manager->isBattleMode() || race_manager->isSoccerMode() ||
|
||||
race_manager->isTutorialMode() || position==1 || position==num_karts)
|
||||
return 1;
|
||||
|
||||
// Now num_karts must be >=3, since position <=num_players
|
||||
|
||||
//Special case for Follow the Leader : top33 is mapped to the first non-leader kart
|
||||
if (race_manager->isFollowMode())
|
||||
{
|
||||
float third = (float) (num_karts-2)/3.0f;
|
||||
|
||||
if(position == 2) return 1;
|
||||
|
||||
if(position <= 2 + third) return (int) RAND_CLASS_RANGE-((RAND_CLASS_RANGE*(position - 2))/third);
|
||||
else if(position <= 2 + 2*third) return (int) RAND_CLASS_RANGE-((RAND_CLASS_RANGE*(position - (2+third)))/third);
|
||||
else return (int) RAND_CLASS_RANGE-((RAND_CLASS_RANGE*(position - (2+2*third)))/third);
|
||||
}
|
||||
|
||||
//Three points divide a line in 4 sections
|
||||
float quarter = (float) (num_karts-1)/4.0f;
|
||||
|
||||
if(position <= 1 + quarter) return (int) RAND_CLASS_RANGE-((RAND_CLASS_RANGE*(position - 1))/quarter);
|
||||
else if(position <= 1 + 2*quarter) return (int) RAND_CLASS_RANGE-((RAND_CLASS_RANGE*(position - (1+quarter)))/quarter);
|
||||
else if(position <= 1 + 3*quarter) return (int) RAND_CLASS_RANGE-((RAND_CLASS_RANGE*(position - (1+2*quarter)))/quarter);
|
||||
else return (int) RAND_CLASS_RANGE-(RAND_CLASS_RANGE*((position - (1+3*quarter)))/quarter);
|
||||
} // convertPositionToClassWeight
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns a random powerup for a kart at a given position. If the race mode
|
||||
* is a battle, the position is actually not used and a randomly selected
|
||||
@ -362,15 +608,24 @@ PowerupManager::PowerupType PowerupManager::getRandomPowerup(unsigned int pos,
|
||||
unsigned int *n,
|
||||
int random_number)
|
||||
{
|
||||
int random_class = random_number%RAND_CLASS_RANGE;
|
||||
|
||||
//First step : select at random a class according to the weights of class for this position
|
||||
// Positions start with 1, while the index starts with 0 - so subtract 1
|
||||
PositionClass pos_class =
|
||||
(race_manager->isBattleMode() ? POSITION_BATTLE_MODE :
|
||||
race_manager->isSoccerMode() ? POSITION_SOCCER_MODE :
|
||||
(race_manager->isTutorialMode() ? POSITION_TUTORIAL_MODE :
|
||||
m_position_to_class[pos-1]));
|
||||
race_manager->isTutorialMode() ? POSITION_TUTORIAL_MODE :
|
||||
random_class > m_position_to_class_cutoff[pos-1] ? m_position_to_class_inf[pos-1] :
|
||||
m_position_to_class_sup[pos-1]);
|
||||
|
||||
int random = random_number%m_powerups_for_position[pos_class].size();
|
||||
int i=m_powerups_for_position[pos_class][random];
|
||||
//Second step : select an item at random
|
||||
|
||||
int random = random_number % m_powerups_for_reference_pos[pos_class].size();
|
||||
int i = m_powerups_for_reference_pos[pos_class][random];
|
||||
|
||||
//int random_item = (random / RAND_CLASS_RANGE) % m_powerups_for_reference_pos[pos_class].size();
|
||||
//int i=m_powerups_for_reference_pos[pos_class][random_item];
|
||||
if(i>=POWERUP_MAX)
|
||||
{
|
||||
i -= POWERUP_MAX;
|
||||
|
@ -44,21 +44,22 @@ namespace irr
|
||||
* items depending on position. The latter is done so that as the first player
|
||||
* you get less advantageous items (but no useless ones either, e.g. anchor),
|
||||
* while as the last you get more useful ones.
|
||||
*
|
||||
* The weight distribution works as follow:
|
||||
* The position in a race is mapped to one of five position classes:
|
||||
* first, top, middle, bottom, last - e.g. for a 6 player game the distribution
|
||||
* is:
|
||||
* position 1 2 3 4 5 6
|
||||
* class first top middle middle bottom last
|
||||
* For each class the weight distribution is read in from powerup.xml:
|
||||
* Depending on the number of karts, 5 reference points are mapped to positions.
|
||||
* For each reference point the weight distribution is read in from powerup.xml:
|
||||
* <!-- bubble cake bowl zipper plunger switch para anvil -->
|
||||
* <last w="0 1 1 2 2 0 2 2" />
|
||||
* So a (well, in this case 'the') player belonging to the class 'last'
|
||||
* will not get a bubble gum or switch. Cakes and bowling balls have
|
||||
* lower probability.
|
||||
* At the start of each race two mappings are computed in updateWeightsForRace:
|
||||
* m_position_to_class maps each postion to the class using the function
|
||||
* convertPositionToClass.
|
||||
* Then, the weights for the real position are calculated as a linear average
|
||||
* between the weights of the reference positions immediately lower and higher.
|
||||
* e.g. ; if the reference positions are 1 and 4,5 ; the 3rd will have
|
||||
* weights equal to (4,5-3)/(4,5-1) times the weight of the reference position
|
||||
* at 4,5 and (3-1)/(4,5-1) times the weight of the reference position at 1.
|
||||
*
|
||||
* At the start of each race three mappings are computed in updateWeightsForRace:
|
||||
* m_position_to_class maps each postion to a list of class using
|
||||
* the function convertPositionToClass, with a class with a higher weight
|
||||
* included more times so picking at random gives us the right class distribution
|
||||
* m_powerups_for_position contains a list of items for each class. A item
|
||||
* with higher weight is included more than once, so at runtime we can
|
||||
* just pick a random item from this list to get the right distribution.
|
||||
@ -100,6 +101,8 @@ public:
|
||||
POSITION_COUNT};
|
||||
|
||||
private:
|
||||
const int RAND_CLASS_RANGE = 1000;
|
||||
|
||||
/** The icon for each powerup. */
|
||||
Material* m_all_icons [POWERUP_MAX];
|
||||
|
||||
@ -112,29 +115,35 @@ private:
|
||||
|
||||
/** For each powerup the weight (probability) used depending on the
|
||||
* number of players. */
|
||||
std::vector<int> m_weights[POSITION_COUNT];
|
||||
std::vector<unsigned int> m_weights[POSITION_COUNT];
|
||||
|
||||
/** A list of all powerups for a specific class. If a powerup
|
||||
* has weight 5, it will be listed 5 times in this list, so
|
||||
* randomly picking an entry from this for a position class will
|
||||
* result in the right distribution of items. */
|
||||
std::vector<PowerupType> m_powerups_for_position[POSITION_COUNT];
|
||||
std::vector<PowerupType> m_powerups_for_reference_pos[POSITION_COUNT];
|
||||
|
||||
/** The mapping of each position to the corresponding position class.
|
||||
* There is one map for each different number of players, so it is
|
||||
* used like m_position_to_class[number_players][position] */
|
||||
std::vector<PositionClass> m_position_to_class;
|
||||
/** The mapping of each position to the corresponding position class. */
|
||||
|
||||
std::vector<PositionClass> m_position_to_class_inf;
|
||||
std::vector<PositionClass> m_position_to_class_sup;
|
||||
std::vector<int> m_position_to_class_cutoff;
|
||||
|
||||
PowerupType getPowerupType(const std::string &name) const;
|
||||
void loadWeights(const XMLNode &root,
|
||||
unsigned int num_karts,
|
||||
const std::string &class_name,
|
||||
PositionClass position_class);
|
||||
void updatePowerupClass(PowerupManager::PositionClass pos_class);
|
||||
PositionClass convertPositionToClass(unsigned int num_karts,
|
||||
unsigned int position, bool class_sup = false);
|
||||
unsigned int convertPositionToClassWeight(unsigned int num_karts,
|
||||
unsigned int position);
|
||||
public:
|
||||
PowerupManager ();
|
||||
~PowerupManager ();
|
||||
void loadAllPowerups ();
|
||||
void loadPowerupsModels ();
|
||||
void loadAllPowerups (unsigned int num_karts);
|
||||
void unloadPowerups ();
|
||||
void LoadPowerup (PowerupType type, const XMLNode &node);
|
||||
void updateWeightsForRace(unsigned int num_karts);
|
||||
|
@ -1791,7 +1791,7 @@ int main(int argc, char *argv[] )
|
||||
material_manager->addSharedMaterial(materials_file);
|
||||
}
|
||||
Referee::init();
|
||||
powerup_manager->loadAllPowerups();
|
||||
powerup_manager->loadPowerupsModels();
|
||||
ItemManager::loadDefaultItemMeshes();
|
||||
|
||||
GUIEngine::addLoadingIcon( irr_driver->getTexture(FileManager::GUI,
|
||||
|
@ -702,6 +702,12 @@ public:
|
||||
return m_minor_mode == MINOR_MODE_TUTORIAL;
|
||||
} // isTutorialMode
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
bool isFollowMode()
|
||||
{
|
||||
return m_minor_mode == MINOR_MODE_FOLLOW_LEADER;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
bool isEggHuntMode()
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user