1 //
2 //  SuperTuxKart - a fun racing game with go-kart
3 //  Copyright (C) 2006-2015 Joerg Henrichs
4 //
5 //  This program is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU General Public License
7 //  as published by the Free Software Foundation; either version 3
8 //  of the License, or (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 #ifndef HEADER_POWERUPMANAGER_HPP
20 #define HEADER_POWERUPMANAGER_HPP
21 
22 #include "utils/leak_check.hpp"
23 #include "utils/no_copy.hpp"
24 #include "utils/types.hpp"
25 
26 #include "btBulletDynamicsCommon.h"
27 
28 #include <atomic>
29 #include <map>
30 #include <string>
31 #include <vector>
32 
33 class Material;
34 class XMLNode;
35 namespace irr
36 {
37     namespace scene { class IMesh; }
38 }
39 
40 /**
41   * \ingroup items
42   */
43 
44 /** This class manages all powerups. It reads in powerup.xml to get the data,
45  *  initialise the static member of some flyables (i.e. powerup.xml contains
46  *  info about cakes, plunger etc which needs to be stored), and maintains
47  *  the 'weights' (used in randomly chosing which item was collected) for all
48  *  items depending on position. The latter is done so that as the first player
49  *  you get less advantageous items (but no useless ones either), while as the
50  *  last you get more useful ones.
51  *
52  *  The weights distribution is described in the powerup.xml file in more
53  *  detail. All weights are stored in the m_all_weights data structure,
54  *  which maps the race mode (race, battle, ...) to a list of WeightsData
55  *  instances. Each WeightsData instance stores the data for one specific
56  *  number of karts. E.g. m_all_weights['race'] contains 5 WeightsData
57  *  instances for 1, 5, 9, 14, and 20 karts.
58  *  At race start a new instance of WeightsData is created in
59  *  m_current_item_weights. It contains the interpolated values for the
60  *  number of karts in the current race (e.g. if the race is with 6 karts
61  *  if will use 3/4 the weights for 5 karts, and 1/4 the weights for 9 karts.
62  *  Then m_current_item_weights will create a weight distribution for each
63  *  possible rank in the race (1 to 6 in the example above). This is the
64  *  interpolation of the values within one WeightsData. Atm there are also
65  *  5 entries in that list (though it does not have to be the same number
66  *  as above - i.e. the 1, 5, 9, 14, 20 weights list). Similarly the actual
67  *  distribution used for a kart with a specific rank is based on dividing
68  *  the available ranks (so 6 karts --> 6 ranks). With the 5 specified values
69  *  the first entry is used for rank 1, the last entry for rank 6, and ranks
70  *  2-5 will be interpolated based on an equal distance: in a race with 6
71  *  karts for example, the 2nd weight list is used for rank 2.25, the 3nd
72  *  for rank 3.5, the 4th for rank 4.75 (and the first and last for rank 1
73  *  and 6). It does not matter that the ranks are non integer: the actual
74  *  weights used for say rank 2, will then be interplated between the weights
75  *  of rank 1 and 2.25 (e.g. 0.8*weights_for 2.25 + 0.2*weights_for 1).
76  */
77 
78 class PowerupManager : public NoCopy
79 {
80 public:
81     LEAK_CHECK();
82 private:
83     // ------------------------------------------------------------------------
84     /** This object stores all the weights for one particular number of
85      *  karts. I.e. it has a list of all the weights within the number of karts.
86      */
87     class WeightsData
88     {
89     private:
90         /** The number of karts for which this entry is to be used. */
91         unsigned int m_num_karts;
92 
93         /** Stores for each of the sections the weights from the XML file. */
94         std::vector < std::vector<int> > m_weights_for_section;
95 
96         /** This field is only populated for the WeightData class that
97          *  is used during a race. It contains for each rank the summed
98          *  weights for easy lookup during a race. */
99         std::vector < std::vector<unsigned> > m_summed_weights_for_rank;
100 
101     public:
102         // The friend declaration gives the PowerupManager access to the
103         // internals, which is ONLY used for testing!!
104         friend PowerupManager;
WeightsData()105         WeightsData() { m_num_karts = 0; }
106         void reset();
107         void readData(int num_karts, const XMLNode *node);
108         void interpolate(WeightsData *prev, WeightsData *next, int num_karts);
109         void convertRankToSection(int rank, int *prev, int *next,
110                                  float *weight);
111         void precomputeWeights();
112         int getRandomItem(int rank, uint64_t random_number);
113         // --------------------------------------------------------------------
114         /** Sets the number of karts. */
setNumKarts(int num_karts)115         void setNumKarts(int num_karts) { m_num_karts = num_karts; }
116         // --------------------------------------------------------------------
117         /** Returns for how many karts this entry is meant for. */
getNumKarts() const118         int getNumKarts() const { return m_num_karts; }
119     };   // class WeightsData
120     // ------------------------------------------------------------------------
121 
122     /** The first key is the race type: race, battle, soccer etc.
123      *  The key then contains a mapping from the kart numbers to the
124      *  WeightsData object that stores all data for the give kart number.
125      */
126     std::map<std::string, std::vector<WeightsData*> > m_all_weights;
127 
128 public:
129     // The anvil and parachute must be at the end of the enum, and the
130     // zipper just before them (see Powerup::hitBonusBox).
131     enum PowerupType {POWERUP_NOTHING,
132                       POWERUP_FIRST,
133                       POWERUP_BUBBLEGUM = POWERUP_FIRST,
134                       POWERUP_CAKE,
135                       POWERUP_BOWLING, POWERUP_ZIPPER, POWERUP_PLUNGER,
136                       POWERUP_SWITCH, POWERUP_SWATTER, POWERUP_RUBBERBALL,
137                       POWERUP_PARACHUTE,
138                       POWERUP_ANVIL,      //powerup.cpp assumes these two come last
139                       POWERUP_LAST=POWERUP_ANVIL,
140                       POWERUP_MAX
141     };
142 
143 private:
144 
145     /** The icon for each powerup. */
146     Material*     m_all_icons [POWERUP_MAX];
147 
148     /** The mesh for each model (if the powerup has a model), e.g. a switch
149         has none. */
150     irr::scene::IMesh *m_all_meshes[POWERUP_MAX];
151 
152     /** The weight distribution to be used for the current race. */
153     WeightsData m_current_item_weights;
154 
155     PowerupType   getPowerupType(const std::string &name) const;
156 
157     /** Seed for random powerup, for local game it will use a random number,
158      *  for network games it will use the start time from server. */
159     std::atomic<uint64_t> m_random_seed;
160 
161 public:
162     static void unitTesting();
163 
164                   PowerupManager  ();
165                  ~PowerupManager  ();
166     void          loadPowerupsModels ();
167     void          loadWeights(const XMLNode *node, const std::string &category);
168     void          unloadPowerups  ();
169     void          computeWeightsForRace(int num_karts);
170     void          loadPowerup     (PowerupType type, const XMLNode &node);
171     PowerupManager::PowerupType
172         getRandomPowerup(unsigned int pos, unsigned int *n,
173                          uint64_t random_number);
174     // ------------------------------------------------------------------------
175     /** Returns the icon(material) for a powerup. */
getIcon(int type) const176     Material* getIcon(int type) const {return m_all_icons [type];}
177     // ------------------------------------------------------------------------
178     /** Returns the mesh for a certain powerup.
179      *  \param type Mesh type for which the model is returned. */
getMesh(int type) const180     irr::scene::IMesh *getMesh(int type) const {return m_all_meshes[type];}
181     // ------------------------------------------------------------------------
getRandomSeed() const182     uint64_t getRandomSeed() const { return m_random_seed.load(); }
183     // ------------------------------------------------------------------------
setRandomSeed(uint64_t seed)184     void setRandomSeed(uint64_t seed) { m_random_seed.store(seed); }
185 
186 };   // class PowerupManager
187 
188 extern PowerupManager* powerup_manager;
189 
190 #endif
191