updated -coop patch
[supertux.git] / contrib / supertux-coop.diff
1 #
2 #  SuperTux -coop patch
3 #  Copyright (C) 2007,2008 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 -p1 < contrib/supertux-coop.diff
31 #
32 #  This patch works for revision 5236. It may break for later revisions.
33 #
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
39 @@ -30,6 +30,7 @@
40  #include "game_session.hpp"
41  #include "console.hpp"
42  #include "gameconfig.hpp"
43 +#include "main.hpp"
44  
45  namespace{
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);
49  
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();
55    } else {
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()
66    end_sequence = 0;
67  
68    main_controller->reset();
69 +  secondary_controller->reset();
70  
71    currentsector = 0;
72  
73 @@ -506,6 +507,22 @@ GameSession::update(float elapsed_time)
74      game_pause = false;
75    }
76  
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);
81 +    if (!p) continue;
82 +    if (p == tux) continue;
83 +    if (p->is_dead()) {
84 +      p->remove_me();
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);
90 +    }
91 +  }
92 +
93    check_end_conditions();
94  
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);
103    }
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);
107 +  }
108  
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");
114    }
115 +  if(secondary_controller) {
116 +    writer.start_list("control-p2");
117 +    secondary_controller->write(writer);
118 +    writer.end_list("control-p2");
119 +  }
120  
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
125 --- a/src/main.cpp
126 +++ b/src/main.cpp
127 @@ -65,6 +65,7 @@ namespace supertux_apple {
128  namespace { DrawingContext *context_pointer; }
129  SDL_Surface *screen;
130  JoystickKeyboardController* main_controller = 0;
131 +JoystickKeyboardController* secondary_controller = 0;
132  TinyGetText::DictionaryManager dictionary_manager;
133  
134  int SCREEN_WIDTH;
135 @@ -541,6 +542,7 @@ int main(int argc, char** argv)
136  
137      timelog("controller");
138      main_controller = new JoystickKeyboardController();
139 +    secondary_controller = new JoystickKeyboardController();
140  
141      timelog("config");
142      init_config();
143 @@ -629,7 +631,9 @@ int main(int argc, char** argv)
144    delete config;
145    config = NULL;
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
155 --- a/src/main.hpp
156 +++ b/src/main.hpp
157 @@ -36,5 +36,6 @@ extern int SCREEN_HEIGHT;
158  // global variables
159  class  JoystickKeyboardController;
160  extern JoystickKeyboardController* main_controller;
161 +extern JoystickKeyboardController* secondary_controller;
162  
163  #endif
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()
170  {
171    main_controller->update();
172 +  secondary_controller->update();
173    Uint8* keystate = SDL_GetKeyState(NULL);
174    SDL_Event event;
175    while(SDL_PollEvent(&event)) 
176      {
177        main_controller->process_event(event);
178 +      secondary_controller->process_event(event);
179  
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)
187  {
188    this->name = 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)
195    else
196      sa_prefix = "small";
197  
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));
201 +  } else {
202 +    sprite->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
203 +  }
204 +
205    /* Set Tux sprite action */
206    if(dying) {
207      sprite->set_action("gameover");
208 @@ -1116,6 +1124,12 @@ Player::collision(GameObject& other, const CollisionHit& hit)
209      return FORCE_MOVE;
210    }
211  
212 +  // Multiple Players pass through one another
213 +  Player* player = dynamic_cast<Player*> (&other);
214 +  if(player) {
215 +    return FORCE_MOVE;
216 +  }
217 +
218    if(hit.left || hit.right) {
219      try_grab(); //grab objects right now, in update it will be too late
220    }
221 @@ -1217,6 +1231,8 @@ Player::kill(bool completely)
222      dying_timer.start(3.0);
223      set_group(COLGROUP_DISABLED);
224  
225 +    if (name == "Penny") return;
226 +
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()
235  
236    add_submenu(_("Setup Joystick"),main_controller->get_joystick_options_menu())
237      ->set_help(_("Configure joystick control-action mappings"));
238 +
239 +  add_submenu(_("Setup Keyboard (P2)"), secondary_controller->get_key_options_menu())
240 +    ->set_help(_("Configure key-action mappings"));
241 +
242 +  add_submenu(_("Setup Joystick (P2)"),secondary_controller->get_joystick_options_menu())
243 +    ->set_help(_("Configure joystick control-action mappings"));
244 +
245    add_hl();
246    add_back(_("Back"));
247  }
248 diff --git a/src/sector.cpp b/src/sector.cpp
249 index 1ff1692..c62f632 100644
250 --- a/src/sector.cpp
251 +++ b/src/sector.cpp
252 @@ -77,11 +77,21 @@ Sector* Sector::_current = 0;
253  bool Sector::show_collrects = false;
254  bool Sector::draw_solids_only = false;
255  
256 +namespace {
257 +  // two-player hack: second player's player_status 
258 +  PlayerStatus* second_player_status = 0;
259 +}
260 +
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)
264  {
265    add_object(new Player(player_status, "Tux"));
266 +
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"));
270 +
271    add_object(new DisplayEffect("Effect"));
272    add_object(new TextObject("Text"));
273  
274 @@ -610,6 +620,16 @@ Sector::activate(const Vector& player_pos)
275      player->move(npos);
276    }
277  
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);
282 +    if (!p) continue;
283 +    if (p == player) continue;
284 +    p->move(player->get_pos());
285 +  }
286 +
287 +
288    camera->reset(player->get_pos());
289    update_game_objects();
290  
291 @@ -670,6 +690,15 @@ Sector::update(float elapsed_time)
292  {
293    player->check_bounds(camera);
294  
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);
299 +    if (!p) continue;
300 +    if (p == player) continue;
301 +    p->check_bounds(camera);
302 +  }
303 +
304    /* update objects */
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);
309    if(player != NULL) {
310      if(this->player != 0) {
311 -      log_warning << "Multiple players added. Ignoring" << std::endl;
312 +      //log_warning << "Multiple players added. Ignoring" << std::endl;
313        return false;
314      }
315      this->player = player;