2 * Copyright (C) 2004 Daniel Heck
3 * Copyright (C) 2006,2007,2008,2009,2010 Ronald Lamprecht
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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "gui/HelpMenu.hh"
28 #include "gui/GameMenu.hh"
29 #include "SoundEngine.hh"
30 #include "SoundEffectManager.hh"
31 #include "MusicManager.hh"
35 #include "StateManager.hh"
36 #include "lev/Index.hh"
37 #include "lev/PersistentIndex.hh"
38 #include "lev/Proxy.hh"
39 #include "lev/RatingManager.hh"
40 #include "lev/ScoreManager.hh"
44 #include "enet/enet.h"
52 using namespace enigma::client
;
56 #include "client_internal.hh"
58 /* -------------------- Auxiliary functions -------------------- */
62 /*! Display a message and change the current mouse speed. */
63 void set_mousespeed (double speed
)
65 int s
= round_nearest
<int>(speed
);
66 options::SetMouseSpeed (s
);
67 s
= round_nearest
<int> (options::GetMouseSpeed ());
68 Msg_ShowText(strf(_("Mouse speed: %d"), s
), false, 2.0);
71 /*! Generate the message that is displayed when the level starts. */
72 std::string
displayedLevelInfo (lev::Proxy
*level
) {
76 tmp
= level
->getLocalizedString("title");
78 tmp
= _("Another nameless level");
79 text
= string("\"")+ tmp
+"\"";
80 tmp
= level
->getAuthor();
82 text
+= _(" by ") + tmp
;
83 tmp
= level
->getLocalizedString("subtitle");
84 if (!tmp
.empty() && tmp
!= "subtitle")
85 text
+= string(" - \"")+ tmp
+ "\"";
86 tmp
= level
->getCredits(false);
88 text
+= string(" - Credits: ")+ tmp
;
89 tmp
= level
->getDedication(false);
91 text
+= string(" - Dedication: ")+ tmp
;
97 /* -------------------- Variables -------------------- */
101 Client client_instance
;
102 const char HSEP
= '^'; // history separator (use character that user cannot use)
105 #define CLIENT client_instance
107 /* -------------------- Client class -------------------- */
110 : m_state (cls_idle
),
112 m_hunt_against_time(0),
124 void Client::init() {
126 for (int i
= 0; i
< 10; i
++) {
127 command
= app
.state
->getString(ecl::strf("CommandHistory#%d", i
).c_str());
128 if (command
.size() > 0)
129 commandHistory
.push_back(command
);
135 void Client::shutdown() {
136 for (int i
= 0; i
< commandHistory
.size(); i
++)
137 app
.state
->setProperty(ecl::strf("CommandHistory#%d", i
).c_str(), commandHistory
[i
].c_str());
140 bool Client::network_start()
145 m_network_host
= enet_host_create (NULL
,
146 1 /* only allow 1 outgoing connection */,
147 57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */,
148 14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */);
150 if (m_network_host
== NULL
) {
152 "An error occurred while trying to create an ENet client host.\n");
157 // ----- Connect to server
159 ENetAddress sv_address
;
162 /* Connect to some.server.net:1234. */
163 enet_address_set_host (&sv_address
, "localhost");
164 sv_address
.port
= 12345;
166 /* Initiate the connection, allocating the two channels 0 and 1. */
167 m_server
= enet_host_connect (m_network_host
, &sv_address
, 2);
169 if (m_server
== NULL
) {
171 "No available peers for initiating an ENet connection.\n");
175 // Wait up to 5 seconds for the connection attempt to succeed.
177 if (enet_host_service (m_network_host
, &event
, 5000) > 0 &&
178 event
.type
== ENET_EVENT_TYPE_CONNECT
)
180 fprintf (stderr
, "Connection to some.server.net:1234 succeeded.");
185 /* Either the 5 seconds are up or a disconnect event was */
186 /* received. Reset the peer in the event the 5 seconds */
187 /* had run out without any significant event. */
188 enet_peer_reset (m_server
);
191 fprintf (stderr
, "Connection to localhost:12345 failed.");
196 void Client::network_stop ()
199 enet_host_destroy (m_network_host
);
201 enet_peer_reset (m_server
);
205 /* ---------- Event handling ---------- */
207 void Client::handle_events()
210 while (SDL_PollEvent(&e
)) {
215 case SDL_MOUSEMOTION
:
216 if (abs(e
.motion
.xrel
) > 300 || abs(e
.motion
.yrel
) > 300) {
217 fprintf(stderr
, "mouse event with %i, %i\n", e
.motion
.xrel
, e
.motion
.yrel
);
220 server::Msg_MouseForce (options::GetDouble("MouseSpeed") *
221 V2 (e
.motion
.xrel
, e
.motion
.yrel
));
223 case SDL_MOUSEBUTTONDOWN
:
224 case SDL_MOUSEBUTTONUP
:
227 case SDL_ACTIVEEVENT
: {
228 update_mouse_button_state();
229 if (e
.active
.gain
== 0 && !video::IsFullScreen())
234 case SDL_VIDEOEXPOSE
: {
235 display::RedrawAll(video::GetScreen());
240 client::Msg_Command("abort");
241 app
.bossKeyPressed
= true;
247 void Client::update_mouse_button_state()
249 int b
= SDL_GetMouseState(0, 0);
250 player::InhibitPickup((b
& SDL_BUTTON(1)) || (b
& SDL_BUTTON(3)));
253 void Client::on_mousebutton(SDL_Event
&e
)
255 if (e
.button
.state
== SDL_PRESSED
) {
256 if (e
.button
.button
== 1) {
257 // left mousebutton -> activate first item in inventory
258 server::Msg_ActivateItem ();
260 else if (e
.button
.button
== 3|| e
.button
.button
== 4) {
261 // right mousebutton, wheel down -> rotate inventory
262 rotate_inventory(+1);
264 else if (e
.button
.button
== 5) {
265 // wheel down -> inverse rotate inventory
266 rotate_inventory(-1);
269 update_mouse_button_state();
272 void Client::rotate_inventory (int direction
)
275 STATUSBAR
->hide_text();
276 player::RotateInventory(direction
);
279 /* -------------------- Console related -------------------- */
281 void Client::process_userinput() {
282 // no addition of existing commands to history
283 if (consoleIndex
== 1) {
284 for (int i
= 0; i
< commandHistory
.size(); i
++) {
285 if (newCommand
== commandHistory
[i
]) {
286 // take existing history command instead of new command
287 consoleIndex
= i
+ 2;
292 // resort history with selected command at bottom
293 if (consoleIndex
== 1) {
294 if (commandHistory
.size() < 10)
295 commandHistory
.push_back(std::string(""));
296 for (int i
= 8; i
>= 0; i
--) {
297 if (i
< commandHistory
.size() - 1)
298 commandHistory
[i
+1] = commandHistory
[i
];
300 } else if (consoleIndex
> 1) {
301 newCommand
= commandHistory
[consoleIndex
- 2];
302 for (int i
= consoleIndex
- 3; i
>= 0; i
--) {
303 if (i
< commandHistory
.size())
304 commandHistory
[i
+1] = commandHistory
[i
];
306 } else { // document history or inventory
309 commandHistory
[0] = newCommand
;
312 STATUSBAR
->hide_text();
313 server::Msg_Command(commandHistory
[0]);
316 void Client::user_input_append(char c
) {
317 if (consoleIndex
<= 0) {
320 } else if (consoleIndex
== 1) {
323 newCommand
= commandHistory
[consoleIndex
- 2] + c
;
326 Msg_ShowText(newCommand
, false);
329 void Client::user_input_backspace() {
330 if (consoleIndex
== 1) {
331 newCommand
.erase(newCommand
.size() - 1, 1);
332 if (!newCommand
.empty())
333 Msg_ShowText(newCommand
, false);
336 STATUSBAR
->hide_text();
338 } else if (consoleIndex
> 1) {
339 newCommand
= commandHistory
[consoleIndex
- 2];
340 newCommand
.erase(newCommand
.size() - 1, 1);
341 if (!newCommand
.empty()) {
343 Msg_ShowText(newCommand
, false);
346 STATUSBAR
->hide_text();
351 void Client::user_input_previous() {
352 if (consoleIndex
< 0) {
354 int docIndex
= documentHistory
.size() + consoleIndex
;
355 if (docIndex
< documentHistory
.size()) {
356 Msg_ShowText(documentHistory
[docIndex
], true);
359 STATUSBAR
->hide_text();
361 } else if (consoleIndex
== 0) {
362 if (newCommand
.length() > 0) {
364 Msg_ShowText(newCommand
, false);
365 } else if (commandHistory
.size() > 0) {
367 Msg_ShowText(commandHistory
[0], false);
369 } else if (consoleIndex
<= commandHistory
.size()) {
371 Msg_ShowText(commandHistory
[consoleIndex
- 2], false);
372 } else { // top of history or new command without history
374 STATUSBAR
->hide_text();
378 void Client::user_input_next() {
379 if (consoleIndex
<= 0) {
381 int docIndex
= documentHistory
.size() + consoleIndex
;
383 Msg_ShowText(documentHistory
[docIndex
], true);
386 STATUSBAR
->hide_text();
388 } else if (consoleIndex
== 1 || (consoleIndex
== 2 && newCommand
.size() == 0)) {
390 STATUSBAR
->hide_text();
391 } else if (consoleIndex
> 1) {
393 Msg_ShowText(consoleIndex
== 1 ? newCommand
: commandHistory
[consoleIndex
- 2], false);
397 void Client::on_keydown(SDL_Event
&e
)
399 SDLKey keysym
= e
.key
.keysym
.sym
;
400 SDLMod keymod
= e
.key
.keysym
.mod
;
402 if (keymod
& KMOD_CTRL
) {
405 server::Msg_Command ("restart");
408 if (keymod
& KMOD_SHIFT
) {
409 // force a reload from file
410 lev::Proxy::releaseCache();
411 server::Msg_Command ("restart");
417 else if (keymod
& KMOD_ALT
) {
422 if (enigma::WizardMode
) {
423 Screen
*scr
= video::GetScreen();
424 ecl::TintRect(scr
->get_surface (), display::GetGameArea(),
430 if (enigma::WizardMode
) {
431 server::Msg_Command ("god");
436 video::TempInputGrab (false);
437 video::ToggleFullscreen ();
445 else if (keymod
& KMOD_META
) {
447 case SDLK_q
: // Mac OS X application quit sequence
448 app
.bossKeyPressed
= true;
458 if (keymod
& KMOD_SHIFT
) {
459 app
.bossKeyPressed
= true;
465 case SDLK_LEFT
: set_mousespeed(options::GetMouseSpeed() - 1); break;
466 case SDLK_RIGHT
: set_mousespeed(options::GetMouseSpeed() + 1); break;
467 case SDLK_TAB
: rotate_inventory(+1); break;
468 case SDLK_F1
: show_help(); break;
473 if (keymod
& KMOD_SHIFT
)
474 server::Msg_Command ("restart");
476 server::Msg_Command ("suicide");
479 case SDLK_F4
: Msg_AdvanceLevel(lev::ADVANCE_STRICTLY
); break;
480 case SDLK_F5
: Msg_AdvanceLevel(lev::ADVANCE_UNSOLVED
); break;
481 case SDLK_F6
: Msg_JumpBack(); break;
484 lev::Proxy
*level
= server::LoadedProxy
;
485 std::string basename
= std::string("screenshots/") +
486 level
->getLocalSubstitutionLevelPath();
487 std::string fname
= basename
+ ".png";
488 std::string fullPath
;
490 while (app
.resourceFS
->findFile(fname
, fullPath
)) {
491 fname
= basename
+ ecl::strf("#%d", i
++) + ".png";
493 std::string savePath
= app
.userImagePath
+ "/" + fname
;
494 video::Screenshot(savePath
);
497 case SDLK_RETURN
: process_userinput(); break;
498 case SDLK_BACKSPACE
: user_input_backspace(); break;
499 case SDLK_UP
: user_input_previous(); break;
500 case SDLK_DOWN
: user_input_next(); break;
502 if (e
.key
.keysym
.unicode
&& (e
.key
.keysym
.unicode
& 0xff80) == 0) {
503 char ascii
= static_cast<char>(e
.key
.keysym
.unicode
& 0x7f);
504 if (isalnum (ascii
) ||
505 strchr(" .-!\"$%&/()=?{[]}\\#'+*~_,;.:<>|", ascii
)) // don't add '^' or change history code
507 user_input_append(ascii
);
516 static const char *helptext_ingame
[] = {
517 N_("Left mouse button:"), N_("Activate/drop leftmost inventory item"),
518 N_("Right mouse button:"), N_("Rotate inventory items"),
519 N_("Escape:"), N_("Show game menu"),
520 N_("Shift+Escape:"), N_("Quit game immediately"),
521 N_("F1:"), N_("Show this help"),
522 N_("F3:"), N_("Kill current marble"),
523 N_("Shift+F3:"), N_("Restart the current level"),
524 N_("F4:"), N_("Skip to next level"),
525 N_("F5:"), 0, // see below
526 N_("F6:"), N_("Jump back to last level"),
527 N_("F10:"), N_("Make screenshot"),
528 N_("Left/right arrow:"), N_("Change mouse speed"),
529 N_("Alt+x:"), N_("Return to level menu"),
530 // N_("Alt+Return:"), N_("Switch between fullscreen and window"),
534 void Client::show_help()
536 server::Msg_Pause (true);
537 video::TempInputGrab
grab(false);
539 helptext_ingame
[17] = app
.state
->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST
540 ? _("Skip to next level for best score hunt")
541 : _("Skip to next unsolved level");
544 gui::displayHelp(helptext_ingame
, 200);
547 update_mouse_button_state();
548 if (m_state
== cls_game
)
549 display::RedrawAll(video::GetScreen());
551 server::Msg_Pause (false);
552 game::ResetGameTimer();
554 if (app
.state
->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST
)
555 server::Msg_Command ("restart"); // inhibit cheating
560 void Client::show_menu(bool isESC
) {
561 if (isESC
&& server::LastMenuTime
!= 0.0 && server::LevelTime
- server::LastMenuTime
< 0.3) {
562 return; // protection against ESC D.o.S. attacks
564 if (isESC
&& server::LastMenuTime
!= 0.0 && server::LevelTime
- server::LastMenuTime
< 0.35) {
566 if (server::MenuCount
> 10)
570 server::Msg_Pause (true);
572 ecl::Screen
*screen
= video::GetScreen();
574 video::TempInputGrab
grab (false);
579 display::GetReferencePointCoordinates(&x
, &y
);
580 enigma::gui::GameMenu(x
, y
).manage();
583 update_mouse_button_state();
584 if (m_state
== cls_game
)
585 display::RedrawAll(screen
);
587 server::Msg_Pause (false);
588 game::ResetGameTimer();
590 if (isESC
) // protection against ESC D.o.S. attacks
591 server::LastMenuTime
= server::LevelTime
;
594 void Client::draw_screen()
598 Screen
*scr
= video::GetScreen();
599 GC
gc (scr
->get_surface());
600 blit(gc
, 0,0, enigma::GetImage("menu_bg", ".jpg"));
601 Font
*f
= enigma::GetFont("menufont");
603 vector
<string
> lines
;
605 ecl::split_copy (m_error_message
, '\n', back_inserter(lines
));
609 const video::VMInfo
*vminfo
= video::GetInfo();
610 int width
= vminfo
->width
- 120;
611 for (unsigned i
=0; i
<lines
.size(); ) {
612 std::string::size_type breakPos
= ecl::breakString (f
, lines
[i
],
614 f
->render(gc
, x
, y
, lines
[i
].substr(0,breakPos
).c_str());
616 if (breakPos
!= lines
[i
].size()) {
617 // process rest of line
618 lines
[i
] = lines
[i
].substr(breakPos
);
625 scr
->flush_updates();
634 std::string
Client::init_hunted_time()
637 m_hunt_against_time
= 0;
638 if (app
.state
->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST
) {
639 lev::Index
*ind
= lev::Index::getCurrentIndex();
640 lev::ScoreManager
*scm
= lev::ScoreManager::instance();
641 lev::Proxy
*curProxy
= ind
->getCurrent();
642 lev::RatingManager
*ratingMgr
= lev::RatingManager::instance();
644 int difficulty
= app
.state
->getInt("Difficulty");
645 int wr_time
= ratingMgr
->getBestScore(curProxy
, difficulty
);
646 int best_user_time
= scm
->getBestUserScore(curProxy
, difficulty
);
648 if (best_user_time
>0 && (wr_time
== -1 || best_user_time
<wr_time
)) {
649 m_hunt_against_time
= best_user_time
;
652 else if (wr_time
>0) {
653 m_hunt_against_time
= wr_time
;
654 hunted
= ratingMgr
->getBestScoreHolder(curProxy
, difficulty
);
657 // STATUSBAR->set_timerstart(-m_hunt_against_time);
662 void Client::tick (double dtime
)
664 const double timestep
= 0.01; // 10ms
670 case cls_preparing_game
: {
671 video::TransitionEffect
*fx
= m_effect
.get();
672 if (fx
&& !fx
->finished()) {
677 server::Msg_StartGame();
681 m_total_game_time
= 0;
688 if (app
.state
->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST
) {
689 int old_second
= round_nearest
<int> (m_total_game_time
);
690 int second
= round_nearest
<int> (m_total_game_time
+ dtime
);
692 if (m_hunt_against_time
&& old_second
<= m_hunt_against_time
) {
693 if (second
> m_hunt_against_time
) { // happens exactly once when par has passed by
694 lev::Index
*ind
= lev::Index::getCurrentIndex();
695 lev::ScoreManager
*scm
= lev::ScoreManager::instance();
696 lev::Proxy
*curProxy
= ind
->getCurrent();
697 lev::RatingManager
*ratingMgr
= lev::RatingManager::instance();
698 int difficulty
= app
.state
->getInt("Difficulty");
699 int wr_time
= ratingMgr
->getBestScore(curProxy
, difficulty
);
700 int best_user_time
= scm
->getBestUserScore(curProxy
, difficulty
);
703 if (wr_time
>0 && (best_user_time
<0 || best_user_time
>wr_time
)) {
704 message
= string(_("Too slow for ")) +
705 ratingMgr
->getBestScoreHolder(curProxy
, difficulty
) +
709 message
= string(_("You are slow today.. [Ctrl-A]"));
712 client::Msg_PlaySound("shatter", 1.0);
713 Msg_ShowText(message
, true, 2.0);
716 if (old_second
<second
&& // tick every second
717 (second
>= (m_hunt_against_time
-5) || // at least 5 seconds
718 second
>= round_nearest
<int> (m_hunt_against_time
*.8))) // or the last 20% before par
720 client::Msg_PlaySound("pickup", 1.0);
726 m_total_game_time
+= dtime
;
727 STATUSBAR
->set_time (m_total_game_time
);
731 for (;m_timeaccu
>= timestep
; m_timeaccu
-= timestep
) {
732 display::Tick (timestep
);
734 display::Redraw(video::GetScreen());
748 while (SDL_PollEvent(&e
)) {
751 app
.bossKeyPressed
= true;
754 client::Msg_Command("abort");
763 void Client::level_finished()
765 lev::Index
*ind
= lev::Index::getCurrentIndex();
766 lev::ScoreManager
*scm
= lev::ScoreManager::instance();
767 lev::Proxy
*curProxy
= ind
->getCurrent();
768 lev::RatingManager
*ratingMgr
= lev::RatingManager::instance();
769 int difficulty
= app
.state
->getInt("Difficulty");
770 int wr_time
= ratingMgr
->getBestScore(curProxy
, difficulty
);
771 int best_user_time
= scm
->getBestUserScore(curProxy
, difficulty
);
772 int par_time
= ratingMgr
->getParScore(curProxy
, difficulty
);
774 int level_time
= round_nearest
<int> (m_total_game_time
);
777 bool timehunt_restart
= false;
779 std::string par_name
= ratingMgr
->getBestScoreHolder(curProxy
, difficulty
);
780 for (int cut
= 2; par_name
.length() > 55; cut
++)
781 par_name
= ratingMgr
->getBestScoreHolder(curProxy
, difficulty
, cut
);
784 if (best_user_time
<0 || best_user_time
>wr_time
) {
785 if (level_time
== wr_time
)
786 text
= string(_("Exactly the world record of "))+par_name
+"!";
787 else if (level_time
<wr_time
)
788 text
= _("Great! A new world record!");
791 if (text
.length() == 0 && best_user_time
>0) {
792 if (level_time
== best_user_time
) {
793 text
= _("Again your personal record...");
794 if (app
.state
->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST
)
795 timehunt_restart
= true; // when hunting yourself: Equal is too slow
797 else if (level_time
<best_user_time
)
798 if (par_time
>= 0 && level_time
<= par_time
)
799 text
= _("New personal record - better than par!");
800 else if (par_time
>= 0)
801 text
= _("New personal record, but over par!");
803 text
= _("New personal record!");
806 if (app
.state
->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST
&&
807 (wr_time
>0 || best_user_time
>0))
809 bool with_par
= best_user_time
== -1 || (wr_time
>0 && wr_time
<best_user_time
);
810 int behind
= level_time
- (with_par
? wr_time
: best_user_time
);
813 if (best_user_time
>0 && level_time
<best_user_time
&& with_par
) {
814 text
= _("Your record, ");
819 text
+= ecl::timeformat(behind
);
821 text
+= _("behind world record.");
823 text
+= _("behind your record.");
825 timehunt_restart
= true; // time hunt failed -> repeat level
829 if (text
.length() == 0) {
830 if (par_time
>= 0 && level_time
<= par_time
)
831 text
= _("Level finished - better than par!");
832 else if (par_time
>= 0)
833 text
= _("Level finished, but over par!");
835 text
= _("Level finished!");
838 text
+= _(" Cheater!");
840 Msg_ShowText(text
, false);
843 scm
->updateUserScore(curProxy
, difficulty
, level_time
);
845 // save score (just in case Enigma crashes when loading next level)
846 lev::ScoreManager::instance()->save();
850 if (timehunt_restart
)
851 server::Msg_Command("restart");
853 m_state
= cls_finished
;
856 void Client::level_loaded(bool isRestart
)
858 lev::Index
*ind
= lev::Index::getCurrentIndex();
859 lev::ScoreManager
*scm
= lev::ScoreManager::instance();
860 lev::Proxy
*curProxy
= ind
->getCurrent();
862 // update window title
863 video::SetCaption(ecl::strf(_("Enigma pack %s - level #%d: %s"), ind
->getName().c_str(),
864 ind
->getCurrentLevel(), curProxy
->getTitle().c_str()).c_str());
866 std::string hunted
= init_hunted_time(); // sets m_hunt_against_time (used below)
867 documentHistory
.clear();
870 // show level information (name, author, etc.)
871 std::string displayed_info
= "";
872 if (m_hunt_against_time
>0) {
874 displayed_info
= _("Your record: ");
876 displayed_info
= _("World record to beat: ");
877 displayed_info
+= ecl::timeformat(m_hunt_against_time
);
878 //+ _(" by ") +hunted;
879 // makes the string too long in many levels
880 Msg_ShowDocument(displayed_info
, true, 4.0);
882 displayed_info
= displayedLevelInfo(curProxy
);
883 Msg_ShowDocument(displayed_info
, true, 2.0);
886 sound::StartLevelMusic();
888 // start screen transition
889 GC
gc(video::BackBuffer());
890 display::DrawAll(gc
);
892 m_effect
.reset (video::MakeEffect ((isRestart
? video::TM_SQUARES
:
893 video::TM_PUSH_RANDOM
), video::BackBuffer()));
895 m_state
= cls_preparing_game
;
899 void Client::handle_message (Message
*m
) { // @@@ unused
901 case CLMSG_LEVEL_LOADED
:
905 fprintf (stderr
, "Unhandled client event: %d\n", m
->type
);
910 void Client::error (const string
&text
)
912 m_error_message
= text
;
917 void Client::registerDocument(std::string text
) {
918 documentHistory
.push_back(text
);
922 void Client::finishedText() {
926 /* -------------------- Functions -------------------- */
928 void client::ClientInit() {
932 void client::ClientShutdown() {
936 bool client::NetworkStart()
938 return CLIENT
.network_start();
941 void client::Msg_LevelLoaded(bool isRestart
)
943 CLIENT
.level_loaded(isRestart
);
946 void client::Tick (double dtime
) {
951 void client::Stop() {
955 void client::Msg_AdvanceLevel (lev::LevelAdvanceMode mode
) {
957 lev::Index
*ind
= lev::Index::getCurrentIndex();
958 // log last played level
959 lev::PersistentIndex::addCurrentToHistory();
961 if (ind
->advanceLevel(mode
)) {
962 // now we may advance
963 server::Msg_LoadLevel(ind
->getCurrent(), false);
966 client::Msg_Command("abort");
969 void client::Msg_JumpBack() {
970 // log last played level
971 lev::PersistentIndex::addCurrentToHistory();
972 server::Msg_JumpBack();
975 bool client::AbortGameP() {
976 return CLIENT
.abort_p();
979 void client::Msg_Command(const string
& cmd
) {
980 if (cmd
== "abort") {
983 else if (cmd
== "level_finished") {
984 client::Msg_PlaySound("finished", 1.0);
985 CLIENT
.level_finished();
987 else if (cmd
== "cheater") {
988 CLIENT
.mark_cheater();
990 else if (cmd
== "easy_going") {
994 enigma::Log
<< "Warning: Client received unknown command '" << cmd
<< "'\n";
998 void client::Msg_PlayerPosition (unsigned iplayer
, const V2
&pos
)
1000 if (iplayer
== (unsigned)player::CurrentPlayer()) {
1001 sound::SetListenerPosition (pos
);
1002 display::SetReferencePoint (pos
);
1006 void client::Msg_PlaySound (const std::string
&wavfile
,
1008 double relative_volume
)
1010 sound::EmitSoundEvent (wavfile
.c_str(), pos
, relative_volume
);
1013 void client::Msg_PlaySound (const std::string
&wavfile
, double relative_volume
)
1015 sound::EmitSoundEvent (wavfile
.c_str(), V2(), relative_volume
);
1018 void client::Msg_Sparkle (const ecl::V2
&pos
) {
1019 display::AddEffect (pos
, "ring-anim", true);
1023 void client::Msg_ShowText(const std::string
&text
, bool scrolling
, double duration
) {
1024 STATUSBAR
->show_text (text
, scrolling
, duration
);
1027 void client::Msg_ShowDocument(const std::string
&text
, bool scrolling
, double duration
) {
1028 CLIENT
.registerDocument(text
);
1029 Msg_ShowText(text
, scrolling
, duration
);
1032 void client::Msg_FinishedText() {
1033 CLIENT
.finishedText();
1036 void client::Msg_Error (const std::string
&text
)
1038 CLIENT
.error (text
);