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 5235. It may break for later revisions.
34 # -----------------------------------------------------------------------------
35 Index: src/control/joystickkeyboardcontroller.cpp
36 ===================================================================
37 --- src/control/joystickkeyboardcontroller.cpp (revision 5235)
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
48 KeyMap::iterator key_mapping = keymap.find(event.key.keysym.sym);
50 // if console key was pressed: toggle console
51 - if ((key_mapping != keymap.end()) && (key_mapping->second == CONSOLE)) {
52 + if ((key_mapping != keymap.end()) && (key_mapping->second == CONSOLE) && (this == main_controller)) {
53 if (event.type == SDL_KEYDOWN)
54 Console::instance->toggle();
56 - if (Console::instance->hasFocus()) {
57 + if (Console::instance->hasFocus() && (this == main_controller)) {
58 // if console is open: send key there
59 process_console_key_event(event);
60 } else if (Menu::current()) {
62 process_menu_key_event(event);
63 } else if(key_mapping == keymap.end()) {
64 // default action: update controls
65 - log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
66 + //log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
68 Control control = key_mapping->second;
69 controls[control] = (event.type == SDL_KEYDOWN);
70 Index: src/options_menu.cpp
71 ===================================================================
72 --- src/options_menu.cpp (revision 5235)
73 +++ src/options_menu.cpp (working copy)
76 add_submenu(_("Setup Keyboard"), main_controller->get_key_options_menu());
77 add_submenu(_("Setup Joystick"),main_controller->get_joystick_options_menu());
78 + add_submenu(std::string(_("Setup Keyboard"))+" (P2)", secondary_controller->get_key_options_menu());
79 + add_submenu(std::string(_("Setup Joystick"))+" (P2)", secondary_controller->get_joystick_options_menu());
84 ===================================================================
85 --- src/main.hpp (revision 5235)
86 +++ src/main.hpp (working copy)
89 class JoystickKeyboardController;
90 extern JoystickKeyboardController* main_controller;
91 +extern JoystickKeyboardController* secondary_controller;
94 Index: src/game_session.cpp
95 ===================================================================
96 --- src/game_session.cpp (revision 5235)
97 +++ src/game_session.cpp (working copy)
101 main_controller->reset();
102 + secondary_controller->reset();
110 + // two-player hack: resurrect Penny when she dies
111 + Player* tux = currentsector->player;
112 + for(std::vector<GameObject*>::iterator i = currentsector->gameobjects.begin(); i != currentsector->gameobjects.end(); ++i) {
113 + Player* p = dynamic_cast<Player*>(*i);
115 + if (p == tux) continue;
116 + if (p->is_dead()) {
118 + static PlayerStatus* ps = new PlayerStatus();
119 + p = new Player(ps, "Penny");
120 + currentsector->add_object(p);
121 + p->move(tux->get_pos());
122 + p->safe_timer.start(TUX_SAFE_TIME);
126 check_end_conditions();
128 // respawning in new sector?
131 // TODO make a screen out of this, another mainloop is ugly
132 main_controller->update();
133 + secondary_controller->update();
135 while (SDL_PollEvent(&event)) {
136 main_controller->process_event(event);
137 + secondary_controller->process_event(event);
138 if(event.type == SDL_QUIT)
141 Index: src/mainloop.cpp
142 ===================================================================
143 --- src/mainloop.cpp (revision 5235)
144 +++ src/mainloop.cpp (working copy)
146 MainLoop::process_events()
148 main_controller->update();
149 + secondary_controller->update();
151 while(SDL_PollEvent(&event)) {
152 main_controller->process_event(event);
153 + secondary_controller->process_event(event);
154 if(Menu::current() != NULL)
155 Menu::current()->event(event);
156 if(event.type == SDL_QUIT)
157 Index: src/object/player.cpp
158 ===================================================================
159 --- src/object/player.cpp (revision 5235)
160 +++ src/object/player.cpp (working copy)
164 controller = main_controller;
165 + if (name == "Penny") controller = secondary_controller;
166 scripting_controller = new CodeController();
167 smalltux_gameover = sprite_manager->create("images/creatures/tux_small/smalltux-gameover.sprite");
168 smalltux_star = sprite_manager->create("images/creatures/tux_small/smalltux-star.sprite");
171 int layer = LAYER_OBJECTS + 1;
173 + // two-player hack: draw Penny in a different color
174 + if (name == "Penny") {
176 + if(tux_body->head) tux_body->head->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
177 + if(tux_body->body) tux_body->body->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
178 + if(tux_body->arms) tux_body->arms->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
179 + if(tux_body->feet) tux_body->feet->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
181 + if(tux_body->head) tux_body->head->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
182 + if(tux_body->body) tux_body->body->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
183 + if(tux_body->arms) tux_body->arms->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
184 + if(tux_body->feet) tux_body->feet->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
187 /* Set Tux sprite action */
190 @@ -1114,6 +1129,12 @@
194 + // Multiple Players pass through one another
195 + Player* player = dynamic_cast<Player*> (&other);
200 if(hit.left || hit.right) {
201 try_grab(); //grab objects right now, in update it will be too late
203 @@ -1206,6 +1227,8 @@
204 dying_timer.start(3.0);
205 set_group(COLGROUP_DISABLED);
207 + if (name == "Penny") return;
209 DisplayEffect* effect = new DisplayEffect();
210 effect->fade_out(3.0);
211 Sector::current()->add_object(effect);
212 Index: src/gameconfig.cpp
213 ===================================================================
214 --- src/gameconfig.cpp (revision 5235)
215 +++ src/gameconfig.cpp (working copy)
217 if(config_control_lisp && main_controller) {
218 main_controller->read(*config_control_lisp);
220 + const lisp::Lisp* config_control_lisp2 = config_lisp->get_lisp("control-p2");
221 + if(config_control_lisp2 && secondary_controller) {
222 + secondary_controller->read(*config_control_lisp2);
228 main_controller->write(writer);
229 writer.end_list("control");
231 + if(secondary_controller) {
232 + writer.start_list("control-p2");
233 + secondary_controller->write(writer);
234 + writer.end_list("control-p2");
237 writer.end_list("supertux-config");
240 ===================================================================
241 --- src/main.cpp (revision 5235)
242 +++ src/main.cpp (working copy)
244 namespace { DrawingContext *context_pointer; }
246 JoystickKeyboardController* main_controller = 0;
247 +JoystickKeyboardController* secondary_controller = 0;
248 TinyGetText::DictionaryManager dictionary_manager;
253 timelog("controller");
254 main_controller = new JoystickKeyboardController();
255 + secondary_controller = new JoystickKeyboardController();
258 timelog("tinygettext");
262 delete main_controller;
263 + delete secondary_controller;
264 main_controller = NULL;
265 + secondary_controller = NULL;
266 delete Console::instance;
267 Console::instance = NULL;
268 Scripting::exit_squirrel();
269 Index: src/sector.cpp
270 ===================================================================
271 --- src/sector.cpp (revision 5235)
272 +++ src/sector.cpp (working copy)
274 bool Sector::show_collrects = false;
275 bool Sector::draw_solids_only = false;
278 + // two-player hack: second player's player_status
279 + PlayerStatus* second_player_status = 0;
282 Sector::Sector(Level* parent)
283 : level(parent), currentmusic(LEVEL_MUSIC),
284 ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), gravity(10.0), player(0), camera(0)
286 add_object(new Player(player_status, "Tux"));
288 + // two-player hack: second player has dummy player_status
289 + if (!second_player_status) second_player_status = new PlayerStatus();
290 + add_object(new Player(second_player_status, "Penny"));
292 add_object(new DisplayEffect("Effect"));
293 add_object(new TextObject("Text"));
299 + // two-player hack: move other players to main player's position
300 + for(GameObjects::iterator i = gameobjects.begin();
301 + i != gameobjects.end(); ++i) {
302 + Player* p = dynamic_cast<Player*>(*i);
304 + if (p == player) continue;
305 + p->move(player->get_pos());
309 camera->reset(player->get_pos());
310 update_game_objects();
314 player->check_bounds(camera);
316 + // two-player hack: keep other players in bound, too
317 + for(GameObjects::iterator i = gameobjects.begin();
318 + i != gameobjects.end(); ++i) {
319 + Player* p = dynamic_cast<Player*>(*i);
321 + if (p == player) continue;
322 + p->check_bounds(camera);
326 for(GameObjects::iterator i = gameobjects.begin();
327 i != gameobjects.end(); ++i) {
329 Player* player = dynamic_cast<Player*> (object);
331 if(this->player != 0) {
332 - log_warning << "Multiple players added. Ignoring" << std::endl;
333 + //log_warning << "Multiple players added. Ignoring" << std::endl;
336 this->player = player;