Added patch that allows cooperative play with two players. Hacky, but lots of fun...
[supertux.git] / contrib / supertux-coop.diff
1 #
2 #  SuperTux -coop patch
3 #  Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
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 2
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 # -----------------------------------------------------------------------------
20 #
21 #  This patch allows two players to cooperatively jump and run through 
22 #  SuperTux' levels.
23 #
24 #  Note that this is more or less a friendly hack. Most objects and all levels
25 #  of SuperTux were not designed to work with two players. Expect lots of bugs.
26 #
27 #  Installing the patch should be pretty straightforward. Simply run the
28 #  following command prior to running jam:
29 #
30 #  patch -p0 < contrib/supertux-coop.diff
31 #
32 #  This patch works for revision 4866. It may break for later revisions.
33 #
34 # -----------------------------------------------------------------------------
35 Index: src/control/joystickkeyboardcontroller.cpp
36 ===================================================================
37 --- src/control/joystickkeyboardcontroller.cpp  (revision 4856)
38 +++ src/control/joystickkeyboardcontroller.cpp  (working copy)
39 @@ -29,6 +29,7 @@
40  #include "game_session.hpp"
41  #include "console.hpp"
42  #include "gameconfig.hpp"
43 +#include "main.hpp"
44  
45  class JoystickKeyboardController::JoystickMenu : public Menu
46  {
47 @@ -339,7 +340,7 @@
48  
49        ButtonMap::iterator i = joy_button_map.find(event.jbutton.button);
50        if(i == joy_button_map.end()) {
51 -        log_debug << "Unmapped joybutton " << (int)event.jbutton.button << " pressed" << std::endl;
52 +        //log_debug << "Unmapped joybutton " << (int)event.jbutton.button << " pressed" << std::endl;
53          return;
54        }
55  
56 @@ -360,13 +361,13 @@
57    // if console key was pressed: toggle console
58    if ((key_mapping != keymap.end()) && (key_mapping->second == CONSOLE)) {
59      if (event.type != SDL_KEYDOWN) return;
60 -    Console::instance->toggle();
61 +    if (this == main_controller) Console::instance->toggle();
62      return;
63    }
64  
65    // if console is open: send key there
66    if (Console::instance->hasFocus()) {
67 -    process_console_key_event(event);
68 +    if (this == main_controller) process_console_key_event(event);
69      return;
70    }
71  
72 @@ -378,7 +379,7 @@
73  
74    // default action: update controls
75    if(key_mapping == keymap.end()) {
76 -    log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
77 +    //log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
78      return;
79    }
80    Control control = key_mapping->second;
81 Index: src/options_menu.cpp
82 ===================================================================
83 --- src/options_menu.cpp        (revision 4856)
84 +++ src/options_menu.cpp        (working copy)
85 @@ -58,6 +58,8 @@
86    }
87    add_submenu(_("Setup Keyboard"), main_controller->get_key_options_menu());
88    add_submenu(_("Setup Joystick"),main_controller->get_joystick_options_menu());
89 +  add_submenu(std::string(_("Setup Keyboard"))+" (P2)", secondary_controller->get_key_options_menu());
90 +  add_submenu(std::string(_("Setup Joystick"))+" (P2)", secondary_controller->get_joystick_options_menu());
91    add_hl();
92    add_back(_("Back"));
93  }
94 Index: src/main.hpp
95 ===================================================================
96 --- src/main.hpp        (revision 4856)
97 +++ src/main.hpp        (working copy)
98 @@ -31,5 +31,6 @@
99  // global variables
100  class JoystickKeyboardController;
101  extern JoystickKeyboardController* main_controller;
102 +extern JoystickKeyboardController* secondary_controller;
103  
104  #endif
105 Index: src/game_session.cpp
106 ===================================================================
107 --- src/game_session.cpp        (revision 4856)
108 +++ src/game_session.cpp        (working copy)
109 @@ -117,6 +117,7 @@
110    end_sequence = 0;
111  
112    main_controller->reset();
113 +  secondary_controller->reset();
114  
115    currentsector = 0;
116  
117 @@ -542,9 +543,11 @@
118  
119      // TODO make a screen out of this, another mainloop is ugly
120      main_controller->update();
121 +    secondary_controller->update();
122      SDL_Event event;
123      while (SDL_PollEvent(&event)) {
124        main_controller->process_event(event);
125 +      secondary_controller->process_event(event);
126        if(event.type == SDL_QUIT)
127          main_loop->quit();
128      }
129 Index: src/mainloop.cpp
130 ===================================================================
131 --- src/mainloop.cpp    (revision 4856)
132 +++ src/mainloop.cpp    (working copy)
133 @@ -166,9 +166,11 @@
134  MainLoop::process_events()
135  {
136    main_controller->update();
137 +  secondary_controller->update();
138    SDL_Event event;
139    while(SDL_PollEvent(&event)) {
140      main_controller->process_event(event);
141 +    secondary_controller->process_event(event);
142      if(Menu::current() != NULL)
143        Menu::current()->event(event);
144      if(event.type == SDL_QUIT)
145 Index: src/object/player.cpp
146 ===================================================================
147 --- src/object/player.cpp       (revision 4856)
148 +++ src/object/player.cpp       (working copy)
149 @@ -116,6 +116,7 @@
150  {
151    this->name = name;
152    controller = main_controller;
153 +  if (name == "Penny") controller = secondary_controller;
154    smalltux_gameover = sprite_manager->create("images/creatures/tux_small/smalltux-gameover.sprite");
155    smalltux_star = sprite_manager->create("images/creatures/tux_small/smalltux-star.sprite");
156    bigtux_star = sprite_manager->create("images/creatures/tux_big/bigtux-star.sprite");
157 @@ -875,6 +876,9 @@
158  
159    int layer = LAYER_OBJECTS + 1;
160  
161 +  // draw second player behind main player
162 +  if (name == "Penny") layer -= 20;
163 +
164    /* Set Tux sprite action */
165    if (climbing)
166      {
167 @@ -1049,6 +1053,12 @@
168      return FORCE_MOVE;
169    }
170  
171 +  // Multiple Players pass through one another
172 +  Player* player = dynamic_cast<Player*> (&other);
173 +  if(player) {
174 +    return FORCE_MOVE;
175 +  }
176 +
177    if(hit.left || hit.right) {
178      try_grab(); //grab objects right now, in update it will be too late
179    }
180 @@ -1141,6 +1151,8 @@
181      dying_timer.start(3.0);
182      set_group(COLGROUP_DISABLED);
183  
184 +    if (name == "Penny") return;
185 +
186      DisplayEffect* effect = new DisplayEffect();
187      effect->fade_out(3.0);
188      Sector::current()->add_object(effect);
189 Index: src/gameconfig.cpp
190 ===================================================================
191 --- src/gameconfig.cpp  (revision 4856)
192 +++ src/gameconfig.cpp  (working copy)
193 @@ -86,6 +86,10 @@
194    if(config_control_lisp && main_controller) {
195      main_controller->read(*config_control_lisp);
196    }
197 +  const lisp::Lisp* config_control_lisp2 = config_lisp->get_lisp("control-p2");
198 +  if(config_control_lisp2 && secondary_controller) {
199 +    secondary_controller->read(*config_control_lisp2);
200 +  }
201  }
202  
203  void
204 @@ -116,6 +120,11 @@
205      main_controller->write(writer);
206      writer.end_list("control");
207    }
208 +  if(secondary_controller) {
209 +    writer.start_list("control-p2");
210 +    secondary_controller->write(writer);
211 +    writer.end_list("control-p2");
212 +  }
213  
214    writer.end_list("supertux-config");
215  }
216 Index: src/main.cpp
217 ===================================================================
218 --- src/main.cpp        (revision 4856)
219 +++ src/main.cpp        (working copy)
220 @@ -58,6 +58,7 @@
221  
222  SDL_Surface* screen = 0;
223  JoystickKeyboardController* main_controller = 0;
224 +JoystickKeyboardController* secondary_controller = 0;
225  TinyGetText::DictionaryManager dictionary_manager;
226  
227  int SCREEN_WIDTH;
228 @@ -514,6 +515,7 @@
229  
230      timelog("controller");
231      main_controller = new JoystickKeyboardController();
232 +    secondary_controller = new JoystickKeyboardController();
233      timelog("config");
234      init_config();
235      timelog("tinygettext");
236 @@ -587,7 +589,9 @@
237    delete config;
238    config = NULL;
239    delete main_controller;
240 +  delete secondary_controller;
241    main_controller = NULL;
242 +  secondary_controller = NULL;
243    delete Console::instance;
244    Console::instance = NULL;
245    Scripting::exit_squirrel();
246 Index: src/sector.cpp
247 ===================================================================
248 --- src/sector.cpp      (revision 4856)
249 +++ src/sector.cpp      (working copy)
250 @@ -74,11 +74,21 @@
251  bool Sector::show_collrects = false;
252  bool Sector::draw_solids_only = false;
253  
254 +namespace {
255 +  // two-player hack: second player's player_status 
256 +  PlayerStatus* second_player_status = 0;
257 +}
258 +
259  Sector::Sector(Level* parent)
260    : level(parent), currentmusic(LEVEL_MUSIC),
261    ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), gravity(10.0), player(0), camera(0)
262  {
263    add_object(new Player(player_status, "Tux"));
264 +
265 +  // two-player hack: second player has dummy player_status
266 +  if (!second_player_status) second_player_status = new PlayerStatus();
267 +  add_object(new Player(second_player_status, "Penny"));
268 +
269    add_object(new DisplayEffect("Effect"));
270    add_object(new TextObject("Text"));
271  
272 @@ -591,6 +601,16 @@
273      player->move(npos);
274    }
275  
276 +  // two-player hack: move other players to main player's position
277 +  for(GameObjects::iterator i = gameobjects.begin();
278 +      i != gameobjects.end(); ++i) {
279 +    Player* p = dynamic_cast<Player*>(*i);
280 +    if (!p) continue;
281 +    if (p == player) continue;
282 +    p->move(player->get_pos());
283 +  }
284 +
285 +
286    camera->reset(player->get_pos());
287    update_game_objects();
288  
289 @@ -639,6 +659,15 @@
290  {
291    player->check_bounds(camera);
292  
293 +  // two-player hack: keep other players in bound, too
294 +  for(GameObjects::iterator i = gameobjects.begin();
295 +      i != gameobjects.end(); ++i) {
296 +    Player* p = dynamic_cast<Player*>(*i);
297 +    if (!p) continue;
298 +    if (p == player) continue;
299 +    p->check_bounds(camera);
300 +  }
301 +
302    /* update objects */
303    for(GameObjects::iterator i = gameobjects.begin();
304            i != gameobjects.end(); ++i) {
305 @@ -721,7 +750,7 @@
306    Player* player = dynamic_cast<Player*> (object);
307    if(player != NULL) {
308      if(this->player != 0) {
309 -      log_warning << "Multiple players added. Ignoring" << std::endl;
310 +      //log_warning << "Multiple players added. Ignoring" << std::endl;
311        return false;
312      }
313      this->player = player;