3 # Copyright (C) 2007,2008 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 -p1 < contrib/supertux-coop.diff
32 # This patch works for revision 5236. It may break for later revisions.
34 # -----------------------------------------------------------------------------
35 diff --git a/src/control/joystickkeyboardcontroller.cpp b/src/control/joystickkeyboardcontroller.cpp
36 index c0ca307..93cbb7c 100644
37 --- a/src/control/joystickkeyboardcontroller.cpp
38 +++ b/src/control/joystickkeyboardcontroller.cpp
40 #include "game_session.hpp"
41 #include "console.hpp"
42 #include "gameconfig.hpp"
46 const int SCAN_JOYSTICKS = Controller::CONTROLCOUNT + 1;
47 @@ -502,11 +503,11 @@ JoystickKeyboardController::process_key_event(const SDL_Event& event)
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()) {
61 diff --git a/src/game_session.cpp b/src/game_session.cpp
62 index f6cf505..93f85da 100644
63 --- a/src/game_session.cpp
64 +++ b/src/game_session.cpp
65 @@ -126,6 +126,7 @@ GameSession::restart_level()
68 main_controller->reset();
69 + secondary_controller->reset();
73 @@ -506,6 +507,22 @@ GameSession::update(float elapsed_time)
77 + // two-player hack: resurrect Penny when she dies
78 + Player* tux = currentsector->player;
79 + for(std::vector<GameObject*>::iterator i = currentsector->gameobjects.begin(); i != currentsector->gameobjects.end(); ++i) {
80 + Player* p = dynamic_cast<Player*>(*i);
82 + if (p == tux) continue;
85 + static PlayerStatus* ps = new PlayerStatus();
86 + p = new Player(ps, "Penny");
87 + currentsector->add_object(p);
88 + p->move(tux->get_pos());
89 + p->safe_timer.start(TUX_SAFE_TIME);
93 check_end_conditions();
95 // respawning in new sector?
96 diff --git a/src/gameconfig.cpp b/src/gameconfig.cpp
97 index 289b6a2..65e8ce2 100644
98 --- a/src/gameconfig.cpp
99 +++ b/src/gameconfig.cpp
100 @@ -108,6 +108,10 @@ Config::load()
101 if(config_control_lisp && main_controller) {
102 main_controller->read(*config_control_lisp);
104 + const lisp::Lisp* config_control_lisp2 = config_lisp->get_lisp("control-p2");
105 + if(config_control_lisp2 && secondary_controller) {
106 + secondary_controller->read(*config_control_lisp2);
109 const lisp::Lisp* config_addons_lisp = config_lisp->get_lisp("addons");
110 if(config_addons_lisp) {
111 @@ -152,6 +156,11 @@ Config::save()
112 main_controller->write(writer);
113 writer.end_list("control");
115 + if(secondary_controller) {
116 + writer.start_list("control-p2");
117 + secondary_controller->write(writer);
118 + writer.end_list("control-p2");
121 writer.start_list("addons");
122 AddonManager::get_instance().write_config(writer);
123 diff --git a/src/main.cpp b/src/main.cpp
124 index fedbe0c..af6dfb6 100644
127 @@ -65,6 +65,7 @@ namespace supertux_apple {
128 namespace { DrawingContext *context_pointer; }
130 JoystickKeyboardController* main_controller = 0;
131 +JoystickKeyboardController* secondary_controller = 0;
132 TinyGetText::DictionaryManager dictionary_manager;
135 @@ -541,6 +542,7 @@ int main(int argc, char** argv)
137 timelog("controller");
138 main_controller = new JoystickKeyboardController();
139 + secondary_controller = new JoystickKeyboardController();
143 @@ -629,7 +631,9 @@ int main(int argc, char** argv)
146 delete main_controller;
147 + delete secondary_controller;
148 main_controller = NULL;
149 + secondary_controller = NULL;
150 delete Console::instance;
151 Console::instance = NULL;
152 Scripting::exit_squirrel();
153 diff --git a/src/main.hpp b/src/main.hpp
154 index 92c1752..917c7f2 100644
157 @@ -36,5 +36,6 @@ extern int SCREEN_HEIGHT;
159 class JoystickKeyboardController;
160 extern JoystickKeyboardController* main_controller;
161 +extern JoystickKeyboardController* secondary_controller;
164 diff --git a/src/mainloop.cpp b/src/mainloop.cpp
165 index ca6ba5e..402737f 100644
166 --- a/src/mainloop.cpp
167 +++ b/src/mainloop.cpp
168 @@ -180,11 +180,13 @@ void
169 MainLoop::process_events()
171 main_controller->update();
172 + secondary_controller->update();
173 Uint8* keystate = SDL_GetKeyState(NULL);
175 while(SDL_PollEvent(&event))
177 main_controller->process_event(event);
178 + secondary_controller->process_event(event);
180 if(Menu::current() != NULL)
181 Menu::current()->event(event);
182 diff --git a/src/object/player.cpp b/src/object/player.cpp
183 index 709aebd..5c4b96d 100644
184 --- a/src/object/player.cpp
185 +++ b/src/object/player.cpp
186 @@ -117,6 +117,7 @@ Player::Player(PlayerStatus* _player_status, const std::string& name)
189 controller = main_controller;
190 + if (name == "Penny") controller = secondary_controller;
191 scripting_controller = new CodeController();
192 sprite = sprite_manager->create("images/creatures/tux/tux.sprite");
193 airarrow.reset(new Surface("images/engine/hud/airarrow.png"));
194 @@ -958,6 +959,13 @@ Player::draw(DrawingContext& context)
198 + // two-player hack: draw Penny in a different color
199 + if (name == "Penny") {
200 + sprite->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
202 + sprite->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
205 /* Set Tux sprite action */
207 sprite->set_action("gameover");
208 @@ -1116,6 +1124,12 @@ Player::collision(GameObject& other, const CollisionHit& hit)
212 + // Multiple Players pass through one another
213 + Player* player = dynamic_cast<Player*> (&other);
218 if(hit.left || hit.right) {
219 try_grab(); //grab objects right now, in update it will be too late
221 @@ -1217,6 +1231,8 @@ Player::kill(bool completely)
222 dying_timer.start(3.0);
223 set_group(COLGROUP_DISABLED);
225 + if (name == "Penny") return;
227 DisplayEffect* effect = new DisplayEffect();
228 effect->fade_out(3.0);
229 Sector::current()->add_object(effect);
230 diff --git a/src/options_menu.cpp b/src/options_menu.cpp
231 index ad1eff7..dbd7a49 100644
232 --- a/src/options_menu.cpp
233 +++ b/src/options_menu.cpp
234 @@ -223,6 +223,13 @@ OptionsMenu::OptionsMenu()
236 add_submenu(_("Setup Joystick"),main_controller->get_joystick_options_menu())
237 ->set_help(_("Configure joystick control-action mappings"));
239 + add_submenu(_("Setup Keyboard (P2)"), secondary_controller->get_key_options_menu())
240 + ->set_help(_("Configure key-action mappings"));
242 + add_submenu(_("Setup Joystick (P2)"),secondary_controller->get_joystick_options_menu())
243 + ->set_help(_("Configure joystick control-action mappings"));
248 diff --git a/src/sector.cpp b/src/sector.cpp
249 index 1ff1692..c62f632 100644
252 @@ -77,11 +77,21 @@ Sector* Sector::_current = 0;
253 bool Sector::show_collrects = false;
254 bool Sector::draw_solids_only = false;
257 + // two-player hack: second player's player_status
258 + PlayerStatus* second_player_status = 0;
261 Sector::Sector(Level* parent)
262 : level(parent), currentmusic(LEVEL_MUSIC),
263 ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), gravity(10.0), player(0), camera(0)
265 add_object(new Player(player_status, "Tux"));
267 + // two-player hack: second player has dummy player_status
268 + if (!second_player_status) second_player_status = new PlayerStatus();
269 + add_object(new Player(second_player_status, "Penny"));
271 add_object(new DisplayEffect("Effect"));
272 add_object(new TextObject("Text"));
274 @@ -610,6 +620,16 @@ Sector::activate(const Vector& player_pos)
278 + // two-player hack: move other players to main player's position
279 + for(GameObjects::iterator i = gameobjects.begin();
280 + i != gameobjects.end(); ++i) {
281 + Player* p = dynamic_cast<Player*>(*i);
283 + if (p == player) continue;
284 + p->move(player->get_pos());
288 camera->reset(player->get_pos());
289 update_game_objects();
291 @@ -670,6 +690,15 @@ Sector::update(float elapsed_time)
293 player->check_bounds(camera);
295 + // two-player hack: keep other players in bound, too
296 + for(GameObjects::iterator i = gameobjects.begin();
297 + i != gameobjects.end(); ++i) {
298 + Player* p = dynamic_cast<Player*>(*i);
300 + if (p == player) continue;
301 + p->check_bounds(camera);
305 for(GameObjects::iterator i = gameobjects.begin();
306 i != gameobjects.end(); ++i) {
307 @@ -764,7 +793,7 @@ Sector::before_object_add(GameObject* object)
308 Player* player = dynamic_cast<Player*> (object);
310 if(this->player != 0) {
311 - log_warning << "Multiple players added. Ignoring" << std::endl;
312 + //log_warning << "Multiple players added. Ignoring" << std::endl;
315 this->player = player;