3 # Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
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.
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.
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.
19 # -----------------------------------------------------------------------------
21 # This patch allows two players to cooperatively jump and run through
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.
27 # Installing the patch should be pretty straightforward. Simply run the
28 # following command prior to running jam:
30 # patch -p0 < contrib/supertux-coop.diff
32 # This patch works for revision 4870. It may break for later revisions.
34 # -----------------------------------------------------------------------------
35 Index: src/control/joystickkeyboardcontroller.cpp
36 ===================================================================
37 --- src/control/joystickkeyboardcontroller.cpp (revision 4870)
38 +++ src/control/joystickkeyboardcontroller.cpp (working copy)
40 #include "game_session.hpp"
41 #include "console.hpp"
42 #include "gameconfig.hpp"
45 class JoystickKeyboardController::JoystickMenu : public Menu
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;
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();
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);
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;
80 Control control = key_mapping->second;
81 Index: src/options_menu.cpp
82 ===================================================================
83 --- src/options_menu.cpp (revision 4870)
84 +++ src/options_menu.cpp (working copy)
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());
95 ===================================================================
96 --- src/main.hpp (revision 4870)
97 +++ src/main.hpp (working copy)
100 class JoystickKeyboardController;
101 extern JoystickKeyboardController* main_controller;
102 +extern JoystickKeyboardController* secondary_controller;
105 Index: src/game_session.cpp
106 ===================================================================
107 --- src/game_session.cpp (revision 4870)
108 +++ src/game_session.cpp (working copy)
112 main_controller->reset();
113 + secondary_controller->reset();
121 + // two-player hack: resurrect Penny when she dies
122 + Player* tux = currentsector->player;
123 + for(std::vector<GameObject*>::iterator i = currentsector->gameobjects.begin(); i != currentsector->gameobjects.end(); ++i) {
124 + Player* p = dynamic_cast<Player*>(*i);
126 + if (p == tux) continue;
127 + if (p->is_dead()) {
129 + static PlayerStatus* ps = new PlayerStatus();
130 + p = new Player(ps, "Penny");
131 + currentsector->add_object(p);
132 + p->move(tux->get_pos());
133 + p->safe_timer.start(TUX_SAFE_TIME);
137 check_end_conditions();
139 // respawning in new sector?
142 // TODO make a screen out of this, another mainloop is ugly
143 main_controller->update();
144 + secondary_controller->update();
146 while (SDL_PollEvent(&event)) {
147 main_controller->process_event(event);
148 + secondary_controller->process_event(event);
149 if(event.type == SDL_QUIT)
152 Index: src/mainloop.cpp
153 ===================================================================
154 --- src/mainloop.cpp (revision 4870)
155 +++ src/mainloop.cpp (working copy)
157 MainLoop::process_events()
159 main_controller->update();
160 + secondary_controller->update();
162 while(SDL_PollEvent(&event)) {
163 main_controller->process_event(event);
164 + secondary_controller->process_event(event);
165 if(Menu::current() != NULL)
166 Menu::current()->event(event);
167 if(event.type == SDL_QUIT)
168 Index: src/object/player.cpp
169 ===================================================================
170 --- src/object/player.cpp (revision 4870)
171 +++ src/object/player.cpp (working copy)
175 controller = main_controller;
176 + if (name == "Penny") controller = secondary_controller;
177 smalltux_gameover = sprite_manager->create("images/creatures/tux_small/smalltux-gameover.sprite");
178 smalltux_star = sprite_manager->create("images/creatures/tux_small/smalltux-star.sprite");
179 bigtux_star = sprite_manager->create("images/creatures/tux_big/bigtux-star.sprite");
182 int layer = LAYER_OBJECTS + 1;
184 + // two-player hack: draw Penny in a different color
185 + if (name == "Penny") {
187 + if(tux_body->head) tux_body->head->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
188 + if(tux_body->body) tux_body->body->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
189 + if(tux_body->arms) tux_body->arms->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
190 + if(tux_body->feet) tux_body->feet->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
192 + if(tux_body->head) tux_body->head->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
193 + if(tux_body->body) tux_body->body->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
194 + if(tux_body->arms) tux_body->arms->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
195 + if(tux_body->feet) tux_body->feet->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
198 /* Set Tux sprite action */
201 @@ -1049,6 +1064,12 @@
205 + // Multiple Players pass through one another
206 + Player* player = dynamic_cast<Player*> (&other);
211 if(hit.left || hit.right) {
212 try_grab(); //grab objects right now, in update it will be too late
214 @@ -1141,6 +1162,8 @@
215 dying_timer.start(3.0);
216 set_group(COLGROUP_DISABLED);
218 + if (name == "Penny") return;
220 DisplayEffect* effect = new DisplayEffect();
221 effect->fade_out(3.0);
222 Sector::current()->add_object(effect);
223 Index: src/gameconfig.cpp
224 ===================================================================
225 --- src/gameconfig.cpp (revision 4870)
226 +++ src/gameconfig.cpp (working copy)
228 if(config_control_lisp && main_controller) {
229 main_controller->read(*config_control_lisp);
231 + const lisp::Lisp* config_control_lisp2 = config_lisp->get_lisp("control-p2");
232 + if(config_control_lisp2 && secondary_controller) {
233 + secondary_controller->read(*config_control_lisp2);
239 main_controller->write(writer);
240 writer.end_list("control");
242 + if(secondary_controller) {
243 + writer.start_list("control-p2");
244 + secondary_controller->write(writer);
245 + writer.end_list("control-p2");
248 writer.end_list("supertux-config");
251 ===================================================================
252 --- src/main.cpp (revision 4870)
253 +++ src/main.cpp (working copy)
256 SDL_Surface* screen = 0;
257 JoystickKeyboardController* main_controller = 0;
258 +JoystickKeyboardController* secondary_controller = 0;
259 TinyGetText::DictionaryManager dictionary_manager;
264 timelog("controller");
265 main_controller = new JoystickKeyboardController();
266 + secondary_controller = new JoystickKeyboardController();
269 timelog("tinygettext");
273 delete main_controller;
274 + delete secondary_controller;
275 main_controller = NULL;
276 + secondary_controller = NULL;
277 delete Console::instance;
278 Console::instance = NULL;
279 Scripting::exit_squirrel();
280 Index: src/sector.cpp
281 ===================================================================
282 --- src/sector.cpp (revision 4870)
283 +++ src/sector.cpp (working copy)
285 bool Sector::show_collrects = false;
286 bool Sector::draw_solids_only = false;
289 + // two-player hack: second player's player_status
290 + PlayerStatus* second_player_status = 0;
293 Sector::Sector(Level* parent)
294 : level(parent), currentmusic(LEVEL_MUSIC),
295 ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), gravity(10.0), player(0), camera(0)
297 add_object(new Player(player_status, "Tux"));
299 + // two-player hack: second player has dummy player_status
300 + if (!second_player_status) second_player_status = new PlayerStatus();
301 + add_object(new Player(second_player_status, "Penny"));
303 add_object(new DisplayEffect("Effect"));
304 add_object(new TextObject("Text"));
310 + // two-player hack: move other players to main player's position
311 + for(GameObjects::iterator i = gameobjects.begin();
312 + i != gameobjects.end(); ++i) {
313 + Player* p = dynamic_cast<Player*>(*i);
315 + if (p == player) continue;
316 + p->move(player->get_pos());
320 camera->reset(player->get_pos());
321 update_game_objects();
325 player->check_bounds(camera);
327 + // two-player hack: keep other players in bound, too
328 + for(GameObjects::iterator i = gameobjects.begin();
329 + i != gameobjects.end(); ++i) {
330 + Player* p = dynamic_cast<Player*>(*i);
332 + if (p == player) continue;
333 + p->check_bounds(camera);
337 for(GameObjects::iterator i = gameobjects.begin();
338 i != gameobjects.end(); ++i) {
340 Player* player = dynamic_cast<Player*> (object);
342 if(this->player != 0) {
343 - log_warning << "Multiple players added. Ignoring" << std::endl;
344 + //log_warning << "Multiple players added. Ignoring" << std::endl;
347 this->player = player;