From: <hik...@us...> - 2010-04-30 01:11:28
|
Revision: 5320 http://supertuxkart.svn.sourceforge.net/supertuxkart/?rev=5320&view=rev Author: hikerstk Date: 2010-04-30 01:11:22 +0000 (Fri, 30 Apr 2010) Log Message: ----------- The probability of which item is collected now depends on the race position. The actual distribution is defined in powerup.xml (and probably needs some adjustments). Modified Paths: -------------- main/trunk/data/powerup.xml main/trunk/src/items/powerup.cpp main/trunk/src/items/powerup.hpp main/trunk/src/items/powerup_manager.cpp main/trunk/src/items/powerup_manager.hpp main/trunk/src/modes/world.cpp main/trunk/src/utils/string_utils.cpp Modified: main/trunk/data/powerup.xml =================================================================== --- main/trunk/data/powerup.xml 2010-04-29 23:17:59 UTC (rev 5319) +++ main/trunk/data/powerup.xml 2010-04-30 01:11:22 UTC (rev 5320) @@ -21,5 +21,15 @@ min-height="0.2" max-height="1.0" force-updown="35" force-to-target="15" max-distance="25" /> + + <!-- Distribution of the items depending on position of the + kart. The order must correspond to powerup_manager.hpp --> + <!-- bubble cake bowl zipper plunger switch para anvil --> + <first w="1 0 0 0 0 1 0 0" /> + <top33 w="1 1 1 1 1 1 1 0" /> + <mid33 w="1 1 1 1 1 1 1 0" /> + <end33 w="0 1 1 1 1 1 1 1" /> + <last w="0 1 1 2 2 0 2 2" /> + </powerup> Modified: main/trunk/src/items/powerup.cpp =================================================================== --- main/trunk/src/items/powerup.cpp 2010-04-29 23:17:59 UTC (rev 5319) +++ main/trunk/src/items/powerup.cpp 2010-04-30 01:11:22 UTC (rev 5320) @@ -289,107 +289,18 @@ */ void Powerup::hitBonusBox(int n, const Item &item, int add_info) { - World *world = World::getWorld(); - //The probabilities of getting the anvil or the parachute increase - //depending on how bad the owner's position is. For the first - //driver the posibility is none, for the last player is 15 %. - if(m_owner->getPosition() != 1 && - m_type == PowerupManager::POWERUP_NOTHING && - world->acceptPowerup(PowerupManager::POWERUP_PARACHUTE) && - world->acceptPowerup(PowerupManager::POWERUP_ANVIL)) - { - // On client: just set the value - if(network_manager->getMode()==NetworkManager::NW_CLIENT) - { - m_random.get(100); // keep random numbers in sync - set( (PowerupManager::PowerupType)add_info, 1); - return; - } - const int SPECIAL_PROB = (int)(15.0 / ((float)world->getCurrentNumKarts() / - (float)m_owner->getPosition())); - const int RAND_NUM = m_random.get(100); - if(RAND_NUM <= SPECIAL_PROB) - { - //If the driver in the first position has finished, give the driver - //the parachute. - for(unsigned int i=0; i < world->getNumKarts(); ++i) - { - Kart *kart = world->getKart(i); - if(kart->isEliminated() || kart == m_owner) continue; - if(kart->getPosition() == 1 && kart->hasFinishedRace()) - { - set(PowerupManager::POWERUP_PARACHUTE, 1); - if(network_manager->getMode()==NetworkManager::NW_SERVER) - { - race_state->itemCollected(m_owner->getWorldKartId(), - item.getItemId(), - m_type); - } - return; - } - } + unsigned int position = m_owner->getPosition(); - set( (m_random.get(2) == 0 ? PowerupManager::POWERUP_ANVIL - : PowerupManager::POWERUP_PARACHUTE),1); + PowerupManager::PowerupType new_powerup = + powerup_manager->getRandomPowerup(position); - if(network_manager->getMode()==NetworkManager::NW_SERVER) - { - race_state->itemCollected(m_owner->getWorldKartId(), - item.getItemId(), - (char)m_type); - } - return; - } - } + World *world = World::getWorld(); - - // If no special case is done: on the client just adjust the number - // dependent on the server informaion: - if(network_manager->getMode()==NetworkManager::NW_CLIENT) - { - if(m_type==PowerupManager::POWERUP_NOTHING) - { - set( (PowerupManager::PowerupType)add_info, n ); - } - else if((PowerupManager::PowerupType)add_info==m_type) - { - m_number+=n; - if(m_number > MAX_POWERUPS) m_number = MAX_POWERUPS; - } - // Ignore new powerup if it is different from the current one - m_random.get(100); // keep random numbers in synch - - return; - } // if network client - - // Otherwise (server or no network): determine powerup randomly - - //(POWERUP_MAX - 1) is the last valid id. We substract 2 because because we have to - //exclude the anvil and the parachute which are handled above, but later we - //have to add 1 to prevent having a value of 0 since that isn't a valid powerup. - PowerupManager::PowerupType newC; - while(true) - { - newC = (PowerupManager::PowerupType) - (m_random.get(PowerupManager::POWERUP_MAX - 1 - 2) + 1); - // allow the game mode to allow or disallow this type of powerup - if(world->acceptPowerup(newC)) break; - } - - // Save the information about the powerup in the race state - // so that the clients can be updated. - if(network_manager->getMode()==NetworkManager::NW_SERVER) - { - race_state->itemCollected(m_owner->getWorldKartId(), - item.getItemId(), - newC); - } - if(m_type==PowerupManager::POWERUP_NOTHING) { - set( newC, n ); + set( new_powerup, n ); } - else if(newC==m_type) + else if(new_powerup==m_type) { m_number+=n; if(m_number > MAX_POWERUPS) Modified: main/trunk/src/items/powerup.hpp =================================================================== --- main/trunk/src/items/powerup.hpp 2010-04-29 23:17:59 UTC (rev 5319) +++ main/trunk/src/items/powerup.hpp 2010-04-30 01:11:22 UTC (rev 5320) @@ -35,12 +35,19 @@ class Powerup { private: + /** A synchronised random number generator for network games. */ RandomGenerator m_random; + + /** Sound effect that is being played. */ SFXBase *m_sound_use; + + /** The powerup type. */ PowerupManager::PowerupType m_type; + + /** Number of collected powerups. */ int m_number; - -protected: + + /** The owner (kart) of this powerup. */ Kart* m_owner; public: @@ -50,6 +57,7 @@ void reset (); Material* getIcon () const; void use (); + void hitBonusBox (int n, const Item &item, int newC=-1); /** Returns the number of powerups. */ int getNum () const {return m_number;} @@ -58,7 +66,6 @@ PowerupManager::PowerupType getType () const {return m_type; } // ------------------------------------------------------------------------ - void hitBonusBox (int n, const Item &item, int newC=-1); }; #endif Modified: main/trunk/src/items/powerup_manager.cpp =================================================================== --- main/trunk/src/items/powerup_manager.cpp 2010-04-29 23:17:59 UTC (rev 5319) +++ main/trunk/src/items/powerup_manager.cpp 2010-04-30 01:11:22 UTC (rev 5320) @@ -31,6 +31,9 @@ #include "items/bowling.hpp" #include "items/cake.hpp" #include "items/plunger.hpp" +#include "modes/world.hpp" +#include "utils/constants.hpp" +#include "utils/string_utils.hpp" PowerupManager* powerup_manager=0; @@ -97,8 +100,15 @@ std::string name; node->get("name", &name); PowerupType type = getPowerupType(name); - LoadPowerup(type, *node); + // The weight nodes will be also included in this list, so ignore those + if(type!=POWERUP_NOTHING) + LoadPowerup(type, *node); } + 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 ); } // loadAllPowerups //----------------------------------------------------------------------------- @@ -142,15 +152,127 @@ Cake::init(node, m_all_meshes[type]); break; default:; } // switch - - const XMLNode *n1 = node.getNode("first"); loadWeights(*n1); - const XMLNode *n2 = node.getNode("top33"); loadWeights(*n2); - const XMLNode *n3 = node.getNode("mid33"); loadWeights(*n3); - const XMLNode *n4 = node.getNode("end33"); loadWeights(*n4); - const XMLNode *n5 = node.getNode("last"); loadWeights(*n5); } // LoadNode // ---------------------------------------------------------------------------- -void PowerupManager::loadWeights(const XMLNode &node) +/** Loads a weight list specified in powerup.xml. The different position + * classes must be loaded in the right order + * \param root The root node of powerup.xml + * \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, + const std::string &class_name, + PositionClass position_class) { + const XMLNode *node = root.getNode(class_name); + std::string s(""); + if(node) node->get("w", &s); + + if(!node || s=="") + { + printf("No weights found for class '%s' - probabilities will be incorrect.\n", + class_name.c_str()); + return; + } + + std::vector<std::string> weight_list = StringUtils::split(s, ' '); + + std::vector<std::string>::iterator i=weight_list.begin(); + while(i!=weight_list.end()) + { + if(*i=="") + { + std::vector<std::string>::iterator next=weight_list.erase(i); + i=next; + } + else + i++; + } + // Fill missing entries with 0s + while(weight_list.size()<(int)POWERUP_LAST) + weight_list.push_back(0); + + if(weight_list.size()!=(int)POWERUP_LAST) + { + printf("Incorrect number of weights found in class '%s':\n", + class_name.c_str()); + printf("%d instead of %d - probabilities will be incorrect.\n", + weight_list.size(), (int)POWERUP_LAST); + return; + } + + for(unsigned int i=0; i<weight_list.size(); i++) + { + int w = atoi(weight_list[i].c_str()); + m_weights[position_class].push_back(w); + } + } // 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 + * 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(); + const World *world = World::getWorld(); + for(unsigned int position =1; position <= num_karts; position++) + { + // Set up the mapping of position to position class: + // ------------------------------------------------- + PositionClass pos_class = convertPositionToClass(num_karts, position); + m_position_to_class.push_back(pos_class); + + // Then determine which items are available: + m_powerups_for_position[pos_class].clear(); + unsigned int sum = 0; + for(unsigned int i= POWERUP_FIRST; i<=POWERUP_LAST; i++) + { + PowerupType type=(PowerupType)i; + if(!world->acceptPowerup(type)) continue; + unsigned int w =m_weights[pos_class][i-POWERUP_FIRST]; + sum += w; + for(unsigned int i=0; i<w; i++) + m_powerups_for_position[pos_class].push_back(type); + } // for type in [POWERUP_FIRST, POWERUP_LAST] + } + +} // updateWeightsForRace + +// ---------------------------------------------------------------------------- +/** Determines the position class for a + */ +PowerupManager::PositionClass + PowerupManager::convertPositionToClass(unsigned int num_karts, + unsigned int position) +{ + if(position==1) return POSITION_FIRST; + if(position==num_karts) return POSITION_LAST; + + // Now num_karts must be >2, 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; + + // num_players-third < position is end33 + if(num_karts - third <= position) return POSITION_END33; + + return POSITION_MID33; +} // convertPositionToClass + +// ---------------------------------------------------------------------------- +PowerupManager::PowerupType PowerupManager::getRandomPowerup(unsigned int pos) +{ + // Positions start with 1, while the index starts with 0 - so subtract 1 + PositionClass pos_class = m_position_to_class[pos-1]; + + int random = rand()%m_powerups_for_position[pos_class].size(); + return m_powerups_for_position[pos_class][random]; +} // getRandomPowerup \ No newline at end of file Modified: main/trunk/src/items/powerup_manager.hpp =================================================================== --- main/trunk/src/items/powerup_manager.hpp 2010-04-29 23:17:59 UTC (rev 5319) +++ main/trunk/src/items/powerup_manager.hpp 2010-04-30 01:11:22 UTC (rev 5320) @@ -33,6 +33,37 @@ /** * \ingroup items */ + +/** This class manages all powerups. It reads in powerup.xml to get the data, + * initialise the static member of some flyables (i.e. powerup.xml contains + * info about cakes, plunger etc which needs to be stored), and maintains + * the 'weights' (used in randomly chosing which item was collected) for all + * 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: + * <!-- 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. + * 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. + * In the example above the list for 'last' will be: + * [cake, bowling,zipper,zipper,plunger,plunger,parachute,parachute, + * anvil,anvil. + */ + class PowerupManager { public: @@ -50,6 +81,15 @@ POWERUP_MAX }; + /** The different position classes, used to map a kart's position to a + * weight distribution for the different powerups. */ + enum PositionClass {POSITION_FIRST, + POSITION_TOP33, + POSITION_MID33, + POSITION_END33, + POSITION_LAST, + POSITION_COUNT}; + private: /** The icon for each powerup. */ Material* m_all_icons [POWERUP_MAX]; @@ -72,26 +112,46 @@ /** For each powerup the weight (probability) used depending on the * number of players. */ - //std::vector<int> m_weight[POWERUP_MAX]; + std::vector<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]; + + /** 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; + PowerupType getPowerupType(const std::string &name) const; - void loadWeights(const XMLNode &node); - + void loadWeights(const XMLNode &root, + const std::string &class_name, + PositionClass position_class); + PositionClass convertPositionToClass(unsigned int num_karts, + unsigned int position); public: PowerupManager (); ~PowerupManager (); void loadAllPowerups (); void removeTextures (); void LoadPowerup (PowerupType type, const XMLNode &node); - Material* getIcon (int type) const {return m_all_icons [type]; } + void updateWeightsForRace(unsigned int num_karts); + Material* getIcon (int type) const {return m_all_icons [type];} + PowerupManager::PowerupType + getRandomPowerup(unsigned int pos); /** Returns the mesh for a certain powerup. * \param type Mesh type for which the model is returned. */ - irr::scene::IMesh *getMesh (int type) const {return m_all_meshes[type]; } - float getForceToTarget(int type) const {return m_all_force_to_target[type]; } + irr::scene::IMesh + *getMesh (int type) const {return m_all_meshes[type];} + float getForceToTarget(int type) const {return m_all_force_to_target[type];} float getMaxDistance (int type) const {return m_all_max_distance[type];} float getMaxTurnAngle (int type) const {return m_all_max_turn_angle[type];} - const btVector3& getExtend (int type) const {return m_all_extends[type]; } + const btVector3& + getExtend (int type) const {return m_all_extends[type];} + }; extern PowerupManager* powerup_manager; Modified: main/trunk/src/modes/world.cpp =================================================================== --- main/trunk/src/modes/world.cpp 2010-04-29 23:17:59 UTC (rev 5319) +++ main/trunk/src/modes/world.cpp 2010-04-30 01:11:22 UTC (rev 5320) @@ -152,6 +152,7 @@ if(!history->replayHistory()) history->initRecording(); network_manager->worldLoaded(); + powerup_manager->updateWeightsForRace(num_karts); // erase messages left over RaceGUI* m = World::getWorld()->getRaceGUI(); if (m) m->clearAllMessages(); Modified: main/trunk/src/utils/string_utils.cpp =================================================================== --- main/trunk/src/utils/string_utils.cpp 2010-04-29 23:17:59 UTC (rev 5319) +++ main/trunk/src/utils/string_utils.cpp 2010-04-30 01:11:22 UTC (rev 5320) @@ -140,7 +140,7 @@ start=i+1; } - else + else // end of string reached { if (keepSplitChar) result.push_back(std::string(s,start-1)); else result.push_back(std::string(s,start)); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |