1 /******************************************************************************
2  *  Warmux is a convivial mass murder game.
3  *  Copyright (C) 2001-2011 Warmux Team.
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (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  *  Teams selection box
20  *****************************************************************************/
21 
22 #include "game/config.h"
23 #include "game/game_mode.h"
24 #include "menu/teams_selection_box.h"
25 #include "menu/team_box.h"
26 #include "gui/grid_box.h"
27 #include "gui/label.h"
28 #include "gui/scroll_box.h"
29 #include "gui/null_widget.h"
30 #include "gui/picture_widget.h"
31 #include "gui/spin_button.h"
32 #include "gui/spin_button_picture.h"
33 #include "gui/text_box.h"
34 #include "gui/vertical_box.h"
35 #include "team/teams_list.h"
36 #include "team/team.h"
37 
38 #include <iostream>
39 
TeamScrollBox(const std::vector<TeamBox * > & teams,const Point2i & size)40 TeamScrollBox::TeamScrollBox(const std::vector<TeamBox*>& teams, const Point2i &size)
41   : ScrollBox(size)
42   , teams(teams)
43   , count(2)
44 {
45   // SetNbTeams not called immediately
46   AddWidget(teams[0]);
47   AddWidget(teams[1]);
48 }
49 
~TeamScrollBox()50 TeamScrollBox::~TeamScrollBox()
51 {
52   // Don't let the vbox delete the items, we're doing it ourselves
53   vbox->Empty();
54 
55   // Destroy widgets
56   for (uint i=0; i<teams.size() ; i++)
57     delete (teams[i]);
58 
59   teams.clear();
60 }
61 
SetNbTeams(uint nb)62 void TeamScrollBox::SetNbTeams(uint nb)
63 {
64   // Reset the list and readd the widget
65   count = 0;
66   vbox->Empty();
67 
68   for (uint i = 0; count < nb; i++) {
69     ASSERT(i < teams.size());
70 
71     // It is easy to have hole in the selection
72     // with network game
73     if (teams[i]->GetTeam()) {
74       AddWidget(teams[i]);
75       count++;
76     }
77   }
78   ASSERT(count == nb);
79 
80   Pack();
81   NeedRedrawing();
82 }
83 
TeamsSelectionBox(const Point2i & _size,bool network,bool w_border)84 TeamsSelectionBox::TeamsSelectionBox(const Point2i &_size, bool network, bool w_border) :
85   HBox(_size.y, w_border, false)
86 {
87   if (!w_border)
88     SetNoBorder();
89   SetMargin(0);
90 
91   // How many teams ?
92   VBox *tmp = new VBox(120, false, false, true);
93   if (network) {
94     local_teams_nb =
95       new SpinButtonWithPicture(_("Local teams:"), "menu/team_number",
96                                 Point2i(100, 130), 0, 1, 0, MAX_NB_TEAMS);
97   } else {
98     local_teams_nb =
99       new SpinButtonWithPicture(_("Number of teams:"), "menu/team_number",
100                                 Point2i(100, 130), 2, 1, 2, MAX_NB_TEAMS);
101   }
102   tmp->AddWidget(local_teams_nb);
103   //tmp->AddWidget(new NullWidget(Point2i(120, 120)));
104   AddWidget(tmp);
105 
106   uint box_w = _size.x - local_teams_nb->GetSizeX() - 10;
107   Point2i grid_size = Point2i(box_w, _size.y);
108   Point2i grid_dim = grid_size / Point2i(300 + 10, 130 + 10);
109   Point2i box_size;
110   bool use_list;
111   if (grid_dim.x*grid_dim.y < (int)MAX_NB_TEAMS) {
112     use_list = true;
113     box_size.SetValues(box_w - 40, 120);
114   } else {
115     use_list = false;
116     box_size.SetValues((grid_size / grid_dim) - 10);
117   }
118 
119   for (uint i=0; i < MAX_NB_TEAMS; i++) {
120     std::string player_name = _("Player") ;
121     char num_player[4];
122     sprintf(num_player, " %d", i+1);
123     player_name += num_player;
124     teams_selections.push_back(new TeamBox(player_name, box_size));
125   }
126 
127   // If the intended gridbox would be too big for the intended size,
128   // instead create a listbox
129   if (use_list) {
130     // Warning: this box takes the ownership of the widgets in teams_selections:
131     // while any other Box will delete the ones it contains, TeamScrollBox
132     // doesn't really contain them as widgets. They therefore aren't released
133     // through this mechanism, but with a manual one. This manual mechanism
134     // requires we have a *real* copy of the vector for when it is destroyed.
135     list_box = new TeamScrollBox(teams_selections, Point2i(box_w-20, _size.y-10));
136     list_box->SetNbTeams(0);
137 
138     AddWidget(list_box);
139   } else {
140     list_box = NULL;
141     Box * teams_grid_box = new GridBox(grid_dim.y, grid_dim.x, 10, false);
142     teams_grid_box->SetNoBorder();
143 
144     for (uint i=0; i<MAX_NB_TEAMS; i++)
145       teams_grid_box->AddWidget(teams_selections[i]);
146 
147     AddWidget(teams_grid_box);
148   }
149 
150   // Load Teams' list
151   GetTeamsList().full_list.sort(compareTeams);
152 }
153 
Draw(const Point2i & mousePosition)154 void TeamsSelectionBox::Draw(const Point2i& mousePosition)
155 {
156   if (list_box)
157     list_box->Draw(mousePosition);
158 }
159 
Click(const Point2i & mousePosition,uint button)160 Widget* TeamsSelectionBox::Click(const Point2i &mousePosition, uint button)
161 {
162   return (list_box) ? list_box->Click(mousePosition, button) : NULL;
163 }
164 
165 // =============================================================================
166 
LocalTeamsSelectionBox(const Point2i & size,bool border)167 LocalTeamsSelectionBox::LocalTeamsSelectionBox(const Point2i &size, bool border) :
168   TeamsSelectionBox(size, false, border)
169 {
170   GetTeamsList().InitList(Config::GetInstance()->AccessTeamList());
171 
172   TeamsList::iterator it  = GetTeamsList().playing_list.begin(),
173     end = GetTeamsList().playing_list.end();
174 
175   uint j=0;
176   for (; it != end && j<teams_selections.size(); ++it, j++) {
177     teams_selections.at(j)->SetTeam((**it), true);
178   }
179 
180   // we need at least 2 teams
181   if (j < 2) {
182     SetNbTeams(2);
183     local_teams_nb->SetValue(2);
184     teams_selections.at(1)->SetAIName(DEFAULT_AI_NAME);
185   } else {
186     SetNbTeams(j);
187     local_teams_nb->SetValue(j);
188   }
189 }
190 
ClickUp(const Point2i & mousePosition,uint button)191 Widget* LocalTeamsSelectionBox::ClickUp(const Point2i &mousePosition, uint button)
192 {
193   if (!Contains(mousePosition))
194     return NULL;
195 
196   if (local_teams_nb->ClickUp(mousePosition, button)) {
197     SetNbTeams(local_teams_nb->GetValue());
198   } else {
199     Widget *w = (list_box) ? list_box->ClickUp(mousePosition, button)
200                            : WidgetList::ClickUp(mousePosition, button);
201 
202     for (uint i=0; i<teams_selections.size() ; i++) {
203 
204       if (teams_selections[i]->Contains(mousePosition)) {
205         Widget * at = teams_selections[i];
206         Rectanglei r(at->GetPosition(), Point2i(38, 38));
207 
208         // Validate where the click really landed
209         if (r.Contains(mousePosition)) {
210           if (button == Mouse::BUTTON_LEFT() || button == SDL_BUTTON_WHEELDOWN) {
211             NextTeam(i);
212           } else if (button == Mouse::BUTTON_RIGHT() || button == SDL_BUTTON_WHEELUP) {
213             PrevTeam(i);
214           }
215           return at;
216         } else {
217           Rectanglei r2(at->GetPositionX(), at->GetPositionY() + 39,
218                         38, 30);
219           if (r2.Contains(mousePosition)) {
220             teams_selections[i]->SwitchPlayerType();
221             return at;
222           }
223         }
224 
225         return w;
226       }
227     }
228 
229     return w;
230   }
231 
232   return NULL;
233 }
234 
PrevTeam(int i)235 void LocalTeamsSelectionBox::PrevTeam(int i)
236 {
237   if (!teams_selections.at(i)->GetTeam())
238     return;
239 
240   bool stop;
241   int  previous_index = -1, index;
242 
243   GetTeamsList().FindById(teams_selections.at(i)->GetTeam()->GetId(), previous_index);
244 
245   index = previous_index-1;
246 
247   do {
248     stop = true;
249 
250     // select the last team if we are outside list
251     if (index < 0)
252       index = int(GetTeamsList().full_list.size())-1;
253 
254     // Get the team at current index
255     Team *tmp = GetTeamsList().FindByIndex(index);
256 
257     // Check if that team is already selected
258     for (int j = 0; j < local_teams_nb->GetValue(); j++) {
259       if (j!= i && tmp == teams_selections.at(j)->GetTeam()) {
260         index--;
261         stop = false;
262         break;
263       }
264     }
265 
266     // We have found a team which is not selected
267     if (tmp && stop)
268       teams_selections.at(i)->SetTeam(*tmp);
269   } while (index != previous_index && !stop);
270 }
271 
NextTeam(int i)272 void LocalTeamsSelectionBox::NextTeam(int i)
273 {
274   if (!teams_selections.at(i)->GetTeam())
275     return;
276 
277   bool to_continue;
278   Team* tmp;
279   int previous_index = -1, index;
280 
281   GetTeamsList().FindById(teams_selections.at(i)->GetTeam()->GetId(), previous_index);
282 
283   index = previous_index+1;
284 
285   do {
286     to_continue = false;
287 
288     // select the first team if we are outside list
289     if (index >= int(GetTeamsList().full_list.size()))
290       index = 0;
291 
292     // Get the team at current index
293     tmp = GetTeamsList().FindByIndex(index);
294 
295     // Check if that team is already selected
296     for (int j = 0; j < local_teams_nb->GetValue(); j++) {
297       if (j!= i && tmp == teams_selections.at(j)->GetTeam()) {
298         index++;
299         to_continue = true;
300         break;
301       }
302     }
303 
304     // We have found a team which is not selected
305     if (tmp != NULL && !to_continue)
306       teams_selections.at(i)->SetTeam(*tmp);
307   } while (index != previous_index && to_continue);
308 }
309 
SetNbTeams(uint nb_teams)310 void LocalTeamsSelectionBox::SetNbTeams(uint nb_teams)
311 {
312   // we hide the useless teams selector
313   for (uint i=nb_teams; i<teams_selections.size(); i++) {
314     teams_selections[i]->ClearTeam();
315   }
316 
317   for (uint i=0; i<nb_teams;i++) {
318     if (!teams_selections.at(i)->GetTeam()) {
319       // we should find an available team
320       teams_selections.at(i)->SetTeam(*(GetTeamsList().FindByIndex(i)));
321       NextTeam(i);
322     }
323   }
324 
325   if (list_box)
326     list_box->SetNbTeams(nb_teams);
327 }
328 
ValidTeamsSelection()329 void LocalTeamsSelectionBox::ValidTeamsSelection()
330 {
331   uint nb_teams=0;
332   for (uint i=0; i < teams_selections.size(); i++) {
333     if (teams_selections.at(i)->GetTeam() != NULL) {
334       nb_teams++;
335     }
336   }
337 
338   if (nb_teams >= 2) {
339     std::list<uint> selection;
340 
341     for (uint i=0; i < teams_selections.size(); i++) {
342 
343       if (teams_selections.at(i)->GetTeam() != NULL) {
344 
345         int index = -1;
346         teams_selections.at(i)->ValidOptions();
347         GetTeamsList().FindById(teams_selections.at(i)->GetTeam()->GetId(), index);
348         if (index > -1)
349         {
350           selection.push_back(uint(index));
351 
352         }
353       }
354     }
355 
356     GetTeamsList().ChangeSelection(selection);
357   }
358 }
359