-#
-# SuperTux -coop patch
-# Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# -----------------------------------------------------------------------------
-#
-# This patch allows two players to cooperatively jump and run through
-# SuperTux' levels.
-#
-# Note that this is more or less a friendly hack. Most objects and all levels
-# of SuperTux were not designed to work with two players. Expect lots of bugs.
-#
-# Installing the patch should be pretty straightforward. Simply run the
-# following command prior to running jam:
-#
-# patch -p0 < contrib/supertux-coop.diff
-#
-# This patch works for revision 4870. It may break for later revisions.
-#
-# -----------------------------------------------------------------------------
-Index: src/control/joystickkeyboardcontroller.cpp
-===================================================================
---- src/control/joystickkeyboardcontroller.cpp (revision 4870)
-+++ src/control/joystickkeyboardcontroller.cpp (working copy)
-@@ -29,6 +29,7 @@
- #include "game_session.hpp"
- #include "console.hpp"
- #include "gameconfig.hpp"
-+#include "main.hpp"
-
- class JoystickKeyboardController::JoystickMenu : public Menu
- {
-@@ -339,7 +340,7 @@
-
- ButtonMap::iterator i = joy_button_map.find(event.jbutton.button);
- if(i == joy_button_map.end()) {
-- log_debug << "Unmapped joybutton " << (int)event.jbutton.button << " pressed" << std::endl;
-+ //log_debug << "Unmapped joybutton " << (int)event.jbutton.button << " pressed" << std::endl;
- return;
- }
+Index: src/control/joystickkeyboardcontroller.cpp\r
+===================================================================\r
+--- src/control/joystickkeyboardcontroller.cpp (revision 6139)\r
++++ src/control/joystickkeyboardcontroller.cpp (working copy)\r
+@@ -479,11 +479,12 @@\r
+ KeyMap::iterator key_mapping = keymap.find(event.key.keysym.sym);
-@@ -360,13 +361,13 @@
// if console key was pressed: toggle console
- if ((key_mapping != keymap.end()) && (key_mapping->second == CONSOLE)) {
- if (event.type != SDL_KEYDOWN) return;
-- Console::instance->toggle();
-+ if (this == main_controller) Console::instance->toggle();
- return;
- }
+- if ((key_mapping != keymap.end()) && (key_mapping->second == CONSOLE)) {
++ // only main controller does console
++ if ((key_mapping != keymap.end()) && (key_mapping->second == CONSOLE) && (this == g_main_controller)) {
+ if (event.type == SDL_KEYDOWN)
+ Console::instance->toggle();
+ } else {
+- if (Console::instance->hasFocus()) {
++ if (Console::instance->hasFocus() && (this == g_main_controller)) {
+ // if console is open: send key there
+ process_console_key_event(event);
+ } else if (MenuManager::current()) {
+Index: src/object/player.cpp\r
+===================================================================\r
+--- src/object/player.cpp (revision 6139)\r
++++ src/object/player.cpp (working copy)\r
+@@ -152,7 +152,7 @@\r
+ climbing(0)
+ {
+ this->name = name;
+- controller = g_main_controller;
++ controller = (name == "Penny") ? g_secondary_controller : g_main_controller;
+ scripting_controller.reset(new CodeController());
+ sprite = sprite_manager->create("images/creatures/tux/tux.sprite");
+ airarrow = Surface::create("images/engine/hud/airarrow.png");
+@@ -1015,6 +1015,16 @@\r
+ else
+ sa_postfix = "-right";
- // if console is open: send key there
- if (Console::instance->hasFocus()) {
-- process_console_key_event(event);
-+ if (this == main_controller) process_console_key_event(event);
- return;
++ // two-player hack: draw Penny in a different color
++ if (name == "Penny") {
++ sprite->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
++ } else {
++ sprite->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
++ }
++ // if/when we have complete penny gfx, we can
++ // load those instead of Tux's sprite in the
++ // constructor
++
+ /* Set Tux sprite action */
+ if(dying) {
+ sprite->set_action("gameover");
+@@ -1177,6 +1187,11 @@\r
+ return FORCE_MOVE;
}
-@@ -378,7 +379,7 @@
-
- // default action: update controls
- if(key_mapping == keymap.end()) {
-- log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
-+ //log_debug << "Key " << event.key.keysym.sym << " is unbound" << std::endl;
- return;
++ Player* player = dynamic_cast<Player*> (&other);
++ if(player) {
++ return ABORT_MOVE;
++ }
++
+ if(hit.left || hit.right) {
+ try_grab(); //grab objects right now, in update it will be too late
}
- Control control = key_mapping->second;
-Index: src/options_menu.cpp
-===================================================================
---- src/options_menu.cpp (revision 4870)
-+++ src/options_menu.cpp (working copy)
-@@ -58,6 +58,8 @@
+@@ -1276,8 +1291,11 @@\r
+ dying_timer.start(3.0);
+ set_group(COLGROUP_DISABLED);
+
+- Sector::current()->effect->fade_out(3.0);
+- sound_manager->stop_music(3.0);
++ // Two-player hack: ignore Penny's death
++ if (name != "Penny") {
++ Sector::current()->effect->fade_out(3.0);
++ sound_manager->stop_music(3.0);
++ }
}
- add_submenu(_("Setup Keyboard"), main_controller->get_key_options_menu());
- add_submenu(_("Setup Joystick"),main_controller->get_joystick_options_menu());
-+ add_submenu(std::string(_("Setup Keyboard"))+" (P2)", secondary_controller->get_key_options_menu());
-+ add_submenu(std::string(_("Setup Joystick"))+" (P2)", secondary_controller->get_joystick_options_menu());
- add_hl();
- add_back(_("Back"));
}
-Index: src/main.hpp
-===================================================================
---- src/main.hpp (revision 4870)
-+++ src/main.hpp (working copy)
-@@ -31,5 +31,6 @@
- // global variables
- class JoystickKeyboardController;
- extern JoystickKeyboardController* main_controller;
-+extern JoystickKeyboardController* secondary_controller;
-
- #endif
-Index: src/game_session.cpp
-===================================================================
---- src/game_session.cpp (revision 4870)
-+++ src/game_session.cpp (working copy)
-@@ -118,6 +118,7 @@
+
+Index: src/supertux/game_session.cpp\r
+===================================================================\r
+--- src/supertux/game_session.cpp (revision 6139)\r
++++ src/supertux/game_session.cpp (working copy)\r
+@@ -93,6 +93,7 @@\r
end_sequence = 0;
- main_controller->reset();
-+ secondary_controller->reset();
+ g_main_controller->reset();
++ g_secondary_controller->reset();
currentsector = 0;
-@@ -448,6 +449,22 @@
- process_events();
- process_menu();
+@@ -434,6 +435,22 @@\r
+ game_pause = false;
+ }
+ // two-player hack: resurrect Penny when she dies
+ Player* tux = currentsector->player;
check_end_conditions();
// respawning in new sector?
-@@ -543,9 +560,11 @@
-
- // TODO make a screen out of this, another mainloop is ugly
- main_controller->update();
-+ secondary_controller->update();
- SDL_Event event;
- while (SDL_PollEvent(&event)) {
- main_controller->process_event(event);
-+ secondary_controller->process_event(event);
- if(event.type == SDL_QUIT)
- main_loop->quit();
- }
-Index: src/mainloop.cpp
-===================================================================
---- src/mainloop.cpp (revision 4870)
-+++ src/mainloop.cpp (working copy)
-@@ -166,9 +166,11 @@
- MainLoop::process_events()
- {
- main_controller->update();
-+ secondary_controller->update();
- SDL_Event event;
- while(SDL_PollEvent(&event)) {
- main_controller->process_event(event);
-+ secondary_controller->process_event(event);
- if(Menu::current() != NULL)
- Menu::current()->event(event);
- if(event.type == SDL_QUIT)
-Index: src/object/player.cpp
-===================================================================
---- src/object/player.cpp (revision 4870)
-+++ src/object/player.cpp (working copy)
-@@ -116,6 +116,7 @@
- {
- this->name = name;
- controller = main_controller;
-+ if (name == "Penny") controller = secondary_controller;
- smalltux_gameover = sprite_manager->create("images/creatures/tux_small/smalltux-gameover.sprite");
- smalltux_star = sprite_manager->create("images/creatures/tux_small/smalltux-star.sprite");
- bigtux_star = sprite_manager->create("images/creatures/tux_big/bigtux-star.sprite");
-@@ -875,6 +876,20 @@
-
- int layer = LAYER_OBJECTS + 1;
-
-+ // two-player hack: draw Penny in a different color
-+ if (name == "Penny") {
-+ layer -= 20;
-+ if(tux_body->head) tux_body->head->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
-+ if(tux_body->body) tux_body->body->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
-+ if(tux_body->arms) tux_body->arms->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
-+ if(tux_body->feet) tux_body->feet->set_color(Color(1.0f, 1.0f, 0.5f, 1.0f));
-+ } else {
-+ if(tux_body->head) tux_body->head->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
-+ if(tux_body->body) tux_body->body->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
-+ if(tux_body->arms) tux_body->arms->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
-+ if(tux_body->feet) tux_body->feet->set_color(Color(1.0f, 1.0f, 1.0f, 1.0f));
-+ }
-+
- /* Set Tux sprite action */
- if (climbing)
- {
-@@ -1049,6 +1064,12 @@
- return FORCE_MOVE;
+Index: src/supertux/gameconfig.cpp\r
+===================================================================\r
+--- src/supertux/gameconfig.cpp (revision 6139)\r
++++ src/supertux/gameconfig.cpp (working copy)\r
+@@ -94,6 +94,11 @@\r
+ g_main_controller->read(*config_control_lisp);
}
-+ // Multiple Players pass through one another
-+ Player* player = dynamic_cast<Player*> (&other);
-+ if(player) {
-+ return FORCE_MOVE;
++ const lisp::Lisp* config_control_p2_lisp = config_lisp->get_lisp("control-p2");
++ if(config_control_p2_lisp && g_secondary_controller) {
++ g_secondary_controller->read(*config_control_p2_lisp);
+ }
+
- if(hit.left || hit.right) {
- try_grab(); //grab objects right now, in update it will be too late
- }
-@@ -1141,6 +1162,8 @@
- dying_timer.start(3.0);
- set_group(COLGROUP_DISABLED);
-
-+ if (name == "Penny") return;
-+
- DisplayEffect* effect = new DisplayEffect();
- effect->fade_out(3.0);
- Sector::current()->add_object(effect);
-Index: src/gameconfig.cpp
-===================================================================
---- src/gameconfig.cpp (revision 4870)
-+++ src/gameconfig.cpp (working copy)
-@@ -86,6 +86,10 @@
- if(config_control_lisp && main_controller) {
- main_controller->read(*config_control_lisp);
- }
-+ const lisp::Lisp* config_control_lisp2 = config_lisp->get_lisp("control-p2");
-+ if(config_control_lisp2 && secondary_controller) {
-+ secondary_controller->read(*config_control_lisp2);
-+ }
- }
-
- void
-@@ -116,6 +120,11 @@
- main_controller->write(writer);
+ const lisp::Lisp* config_addons_lisp = config_lisp->get_lisp("addons");
+ if(config_addons_lisp) {
+ AddonManager::get_instance().read(*config_addons_lisp);
+@@ -138,6 +143,12 @@\r
writer.end_list("control");
}
-+ if(secondary_controller) {
+
++ if(g_secondary_controller) {
+ writer.start_list("control-p2");
-+ secondary_controller->write(writer);
++ g_secondary_controller->write(writer);
+ writer.end_list("control-p2");
+ }
++
+ writer.start_list("addons");
+ AddonManager::get_instance().write(writer);
+ writer.end_list("addons");
+Index: src/supertux/globals.cpp\r
+===================================================================\r
+--- src/supertux/globals.cpp (revision 6139)\r
++++ src/supertux/globals.cpp (working copy)\r
+@@ -19,6 +19,7 @@\r
- writer.end_list("supertux-config");
- }
-Index: src/main.cpp
-===================================================================
---- src/main.cpp (revision 4870)
-+++ src/main.cpp (working copy)
-@@ -58,6 +58,7 @@
-
- SDL_Surface* screen = 0;
- JoystickKeyboardController* main_controller = 0;
-+JoystickKeyboardController* secondary_controller = 0;
- TinyGetText::DictionaryManager dictionary_manager;
+ SDL_Surface* g_screen;
+ JoystickKeyboardController* g_main_controller = 0;
++JoystickKeyboardController* g_secondary_controller = 0;
+ tinygettext::DictionaryManager* dictionary_manager = 0;
int SCREEN_WIDTH;
-@@ -514,6 +515,7 @@
+Index: src/supertux/globals.hpp\r
+===================================================================\r
+--- src/supertux/globals.hpp (revision 6139)\r
++++ src/supertux/globals.hpp (working copy)\r
+@@ -41,6 +41,7 @@\r
+
+ // global variables
+ extern JoystickKeyboardController* g_main_controller;
++extern JoystickKeyboardController* g_secondary_controller;
+
+ extern SDL_Surface* g_screen;
+
+@@ -64,6 +65,7 @@\r
+
+ // global player state
+ extern PlayerStatus* player_status;
++extern PlayerStatus* second_player_status;
+
+ extern SpriteManager* sprite_manager;
+
+Index: src/supertux/main.cpp\r
+===================================================================\r
+--- src/supertux/main.cpp (revision 6139)\r
++++ src/supertux/main.cpp (working copy)\r
+@@ -541,6 +541,7 @@\r
timelog("controller");
- main_controller = new JoystickKeyboardController();
-+ secondary_controller = new JoystickKeyboardController();
+ g_main_controller = new JoystickKeyboardController();
++ g_secondary_controller = new JoystickKeyboardController();
+
timelog("config");
init_config();
- timelog("tinygettext");
-@@ -587,7 +589,9 @@
- delete config;
- config = NULL;
- delete main_controller;
-+ delete secondary_controller;
- main_controller = NULL;
-+ secondary_controller = NULL;
+@@ -629,6 +630,8 @@\r
+ g_config = NULL;
+ delete g_main_controller;
+ g_main_controller = NULL;
++ delete g_secondary_controller;
++ g_secondary_controller = NULL;
delete Console::instance;
Console::instance = NULL;
- Scripting::exit_squirrel();
-Index: src/sector.cpp
-===================================================================
---- src/sector.cpp (revision 4870)
-+++ src/sector.cpp (working copy)
-@@ -74,11 +74,21 @@
- bool Sector::show_collrects = false;
- bool Sector::draw_solids_only = false;
-
-+namespace {
-+ // two-player hack: second player's player_status
-+ PlayerStatus* second_player_status = 0;
+ scripting::exit_squirrel();
+Index: src/supertux/menu/menu_storage.cpp\r
+===================================================================\r
+--- src/supertux/menu/menu_storage.cpp (revision 6139)\r
++++ src/supertux/menu/menu_storage.cpp (working copy)\r
+@@ -26,6 +26,8 @@\r
+ ProfileMenu* MenuStorage::profile_menu = 0;
+ KeyboardMenu* MenuStorage::key_options_menu = 0;
+ JoystickMenu* MenuStorage::joystick_options_menu = 0;
++KeyboardMenu* MenuStorage::key_options_p2_menu = 0;
++JoystickMenu* MenuStorage::joystick_options_p2_menu = 0;
+
+ OptionsMenu*
+ MenuStorage::get_options_menu()
+@@ -63,4 +65,26 @@\r
+ return joystick_options_menu;
+ }
+
++KeyboardMenu*
++MenuStorage::get_key_options_p2_menu()
++{
++ if (!key_options_p2_menu)
++ { // FIXME: this in never freed
++ key_options_p2_menu = new KeyboardMenu(g_secondary_controller);
++ }
++
++ return key_options_p2_menu;
+}
+
- Sector::Sector(Level* parent)
- : level(parent), currentmusic(LEVEL_MUSIC),
- ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), gravity(10.0), player(0), camera(0)
++JoystickMenu*
++MenuStorage::get_joystick_options_p2_menu()
++{
++ if (!joystick_options_p2_menu)
++ { // FIXME: this in never freed
++ joystick_options_p2_menu = new JoystickMenu(g_secondary_controller);
++ }
++
++ return joystick_options_p2_menu;
++}
++
+ /* EOF */
+Index: src/supertux/menu/menu_storage.hpp\r
+===================================================================\r
+--- src/supertux/menu/menu_storage.hpp (revision 6139)\r
++++ src/supertux/menu/menu_storage.hpp (working copy)\r
+@@ -32,12 +32,16 @@\r
+ static ProfileMenu* get_profile_menu();
+ static KeyboardMenu* get_key_options_menu();
+ static JoystickMenu* get_joystick_options_menu();
++ static KeyboardMenu* get_key_options_p2_menu();
++ static JoystickMenu* get_joystick_options_p2_menu();
+
+ private:
+ static OptionsMenu* options_menu;
+ static ProfileMenu* profile_menu;
+ static KeyboardMenu* key_options_menu;
+ static JoystickMenu* joystick_options_menu;
++ static KeyboardMenu* key_options_p2_menu;
++ static JoystickMenu* joystick_options_p2_menu;
+
+ private:
+ MenuStorage(const MenuStorage&);
+Index: src/supertux/menu/options_menu.cpp\r
+===================================================================\r
+--- src/supertux/menu/options_menu.cpp (revision 6139)\r
++++ src/supertux/menu/options_menu.cpp (working copy)\r
+@@ -161,6 +161,13 @@\r
+
+ add_submenu(_("Setup Joystick"), MenuStorage::get_joystick_options_menu())
+ ->set_help(_("Configure joystick control-action mappings"));
++
++ add_submenu(_("Setup P2 Keyboard"), MenuStorage::get_key_options_p2_menu())
++ ->set_help(_("Configure key-action mappings"));
++
++ add_submenu(_("Setup P2 Joystick"), MenuStorage::get_joystick_options_p2_menu())
++ ->set_help(_("Configure joystick control-action mappings"));
++
+ add_hl();
+ add_back(_("Back"));
+ }
+Index: src/supertux/player_status.cpp\r
+===================================================================\r
+--- src/supertux/player_status.cpp (revision 6139)\r
++++ src/supertux/player_status.cpp (working copy)\r
+@@ -30,6 +30,7 @@\r
+ static const int MAX_COINS = 9999;
+
+ PlayerStatus* player_status = 0;
++PlayerStatus* second_player_status = 0;
+
+ PlayerStatus::PlayerStatus() :
+ coins(START_COINS),
+Index: src/supertux/resources.cpp\r
+===================================================================\r
+--- src/supertux/resources.cpp (revision 6139)\r
++++ src/supertux/resources.cpp (working copy)\r
+@@ -48,6 +48,7 @@\r
+ sprite_manager = new SpriteManager();
+
+ player_status = new PlayerStatus();
++ second_player_status = new PlayerStatus();
+ }
+
+ /* Free shared data: */
+@@ -67,6 +68,9 @@\r
+
+ delete player_status;
+ player_status = NULL;
++
++ delete second_player_status;
++ second_player_status = NULL;
+ }
+
+ /* EOF */
+Index: src/supertux/screen_manager.cpp\r
+===================================================================\r
+--- src/supertux/screen_manager.cpp (revision 6139)\r
++++ src/supertux/screen_manager.cpp (working copy)\r
+@@ -190,11 +190,13 @@\r
+ ScreenManager::process_events()
+ {
+ g_main_controller->update();
++ g_secondary_controller->update();
+ Uint8* keystate = SDL_GetKeyState(NULL);
+ SDL_Event event;
+ while(SDL_PollEvent(&event))
+ {
+ g_main_controller->process_event(event);
++ g_secondary_controller->process_event(event);
+
+ if(MenuManager::current() != NULL)
+ MenuManager::current()->event(event);
+Index: src/supertux/sector.cpp\r
+===================================================================\r
+--- src/supertux/sector.cpp (revision 6139)\r
++++ src/supertux/sector.cpp (working copy)\r
+@@ -84,6 +84,7 @@\r
+ effect(0)
{
add_object(new Player(player_status, "Tux"));
-+
-+ // two-player hack: second player has dummy player_status
-+ if (!second_player_status) second_player_status = new PlayerStatus();
+ add_object(new Player(second_player_status, "Penny"));
-+
add_object(new DisplayEffect("Effect"));
add_object(new TextObject("Text"));
-@@ -591,6 +601,16 @@
- player->move(npos);
+@@ -572,19 +573,27 @@\r
}
+ try_expose_me();
+
+- // spawn smalltux below spawnpoint
+- if (!player->is_big()) {
+- player->move(player_pos + Vector(0,32));
+- } else {
+- player->move(player_pos);
+- }
+- // spawning tux in the ground would kill him
+- if(!is_free_of_tiles(player->get_bbox())) {
+- log_warning << "Tried spawning Tux in solid matter. Compensating." << std::endl;
+- Vector npos = player->get_bbox().p1;
+- npos.y-=32;
+- player->move(npos);
+ // two-player hack: move other players to main player's position
+ for(GameObjects::iterator i = gameobjects.begin();
+ i != gameobjects.end(); ++i) {
+ Player* p = dynamic_cast<Player*>(*i);
+ if (!p) continue;
-+ if (p == player) continue;
-+ p->move(player->get_pos());
-+ }
+
++ // spawn smalltux below spawnpoint
++ if (!p->is_big()) {
++ p->move(player_pos + Vector(0,32));
++ } else {
++ p->move(player_pos);
++ }
+
- camera->reset(player->get_pos());
- update_game_objects();
++ // spawning tux in the ground would kill him
++ if(!is_free_of_tiles(p->get_bbox())) {
++ log_warning << "Tried spawning Tux in solid matter. Compensating." << std::endl;
++ Vector npos = p->get_bbox().p1;
++ npos.y-=32;
++ p->move(npos);
++ }
+ }
-@@ -639,6 +659,15 @@
+ camera->reset(player->get_pos());
+@@ -645,7 +654,13 @@\r
+ void
+ Sector::update(float elapsed_time)
{
- player->check_bounds(camera);
-
-+ // two-player hack: keep other players in bound, too
+- player->check_bounds(camera);
++ // keep players in bounds
+ for(GameObjects::iterator i = gameobjects.begin();
+ i != gameobjects.end(); ++i) {
+ Player* p = dynamic_cast<Player*>(*i);
+ if (!p) continue;
-+ if (p == player) continue;
+ p->check_bounds(camera);
+ }
-+
+
/* update objects */
for(GameObjects::iterator i = gameobjects.begin();
- i != gameobjects.end(); ++i) {
-@@ -721,7 +750,7 @@
+@@ -741,10 +756,11 @@\r
Player* player = dynamic_cast<Player*> (object);
if(player != NULL) {
if(this->player != 0) {
- log_warning << "Multiple players added. Ignoring" << std::endl;
-+ //log_warning << "Multiple players added. Ignoring" << std::endl;
- return false;
+- return false;
++// log_warning << "Multiple players added. Ignoring" << std::endl;
++// return false;
++ } else {
++ this->player = player;
}
- this->player = player;
+- this->player = player;
+ }
+
+ DisplayEffect* effect = dynamic_cast<DisplayEffect*> (object);