Files
commandergenius/project/jni/application/enigma/src/client.cpp

1006 lines
29 KiB
C++
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (C) 2004 Daniel Heck
* Copyright (C) 2006, 2007 Ronald Lamprecht
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "client.hh"
#include "game.hh"
#include "display.hh"
#include "options.hh"
#include "server.hh"
#include "gui/HelpMenu.hh"
#include "main.hh"
#include "gui/GameMenu.hh"
#include "sound.hh"
#include "player.hh"
#include "world.hh"
#include "nls.hh"
#include "StateManager.hh"
#include "lev/Index.hh"
#include "lev/PersistentIndex.hh"
#include "lev/Proxy.hh"
#include "lev/RatingManager.hh"
#include "lev/ScoreManager.hh"
#include "ecl_sdl.hh"
#include "enet/enet.h"
#include <cctype>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <iostream>
using namespace enigma::client;
using namespace ecl;
using namespace std;
#ifdef ANDROID
#include <SDL.h>
#endif
#include "client_internal.hh"
/* -------------------- Auxiliary functions -------------------- */
namespace
{
/*! Display a message and change the current mouse speed. */
void set_mousespeed (double speed)
{
int s = round_nearest<int>(speed);
options::SetMouseSpeed (s);
s = round_nearest<int> (options::GetMouseSpeed ());
Msg_ShowText(strf(_("Mouse speed: %d"), s), false, 2.0);
}
/*! Generate the message that is displayed when the level starts. */
string displayedLevelInfo (lev::Proxy *level)
{
std::string text;
std::string tmp;
tmp = level->getLocalizedString("title");
if (tmp.empty())
tmp = _("Another nameless level");
text = string("\"")+ tmp +"\"";
tmp = level->getAuthor();
if (!tmp.empty())
text += _(" by ") + tmp;
tmp = level->getLocalizedString("subtitle");
if (!tmp.empty() && tmp != "subtitle")
text += string(" - \"")+ tmp + "\"";
tmp = level->getCredits(false);
if (!tmp.empty())
text += string(" - Credits: ")+ tmp;
tmp = level->getDedication(false);
if (!tmp.empty())
text += string(" - Dedication: ")+ tmp;
return text;
}
}
/* -------------------- Variables -------------------- */
namespace
{
Client client_instance;
const char HSEP = '^'; // history separator (use character that user cannot use)
}
#define CLIENT client_instance
/* -------------------- Client class -------------------- */
Client::Client()
: m_state (cls_idle),
m_levelname(),
m_hunt_against_time(0),
m_cheater(false),
m_user_input()
{
m_network_host = 0;
}
Client::~Client()
{
network_stop();
}
bool Client::network_start()
{
if (m_network_host)
return true;
m_network_host = enet_host_create (NULL,
1 /* only allow 1 outgoing connection */,
57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */,
14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */);
if (m_network_host == NULL) {
fprintf (stderr,
"An error occurred while trying to create an ENet client host.\n");
return false;
}
// ----- Connect to server
ENetAddress sv_address;
ENetPeer *m_server;
/* Connect to some.server.net:1234. */
enet_address_set_host (&sv_address, "localhost");
sv_address.port = 12345;
/* Initiate the connection, allocating the two channels 0 and 1. */
m_server = enet_host_connect (m_network_host, &sv_address, 2);
if (m_server == NULL) {
fprintf (stderr,
"No available peers for initiating an ENet connection.\n");
return false;
}
// Wait up to 5 seconds for the connection attempt to succeed.
ENetEvent event;
if (enet_host_service (m_network_host, &event, 5000) > 0 &&
event.type == ENET_EVENT_TYPE_CONNECT)
{
fprintf (stderr, "Connection to some.server.net:1234 succeeded.");
return true;
}
else
{
/* Either the 5 seconds are up or a disconnect event was */
/* received. Reset the peer in the event the 5 seconds */
/* had run out without any significant event. */
enet_peer_reset (m_server);
m_server = 0;
fprintf (stderr, "Connection to localhost:12345 failed.");
return false;
}
}
void Client::network_stop ()
{
if (m_network_host)
enet_host_destroy (m_network_host);
if (m_server)
enet_peer_reset (m_server);
}
/* ---------- Event handling ---------- */
void Client::handle_events()
{
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_KEYDOWN:
on_keydown(e);
break;
case SDL_MOUSEMOTION:
#ifndef ANDROID
if (abs(e.motion.xrel) > 300 || abs(e.motion.yrel) > 300) {
fprintf(stderr, "mouse event with %i, %i\n", e.motion.xrel, e.motion.yrel);
}
else
server::Msg_MouseForce (options::GetDouble("MouseSpeed") *
V2 (e.motion.xrel, e.motion.yrel));
#endif
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
on_mousebutton(e);
break;
case SDL_ACTIVEEVENT: {
update_mouse_button_state();
if (e.active.gain == 0 && !video::IsFullScreen())
show_menu();
break;
}
case SDL_VIDEOEXPOSE: {
display::RedrawAll(video::GetScreen());
break;
}
case SDL_QUIT:
client::Msg_Command("abort");
break;
}
}
}
void Client::update_mouse_button_state()
{
int b = SDL_GetMouseState(0, 0);
player::InhibitPickup((b & SDL_BUTTON(1)) || (b & SDL_BUTTON(3)));
}
void Client::on_mousebutton(SDL_Event &e)
{
if (e.button.state == SDL_PRESSED) {
if (e.button.button == 1) {
// left mousebutton -> activate first item in inventory
server::Msg_ActivateItem ();
}
else if (e.button.button == 3|| e.button.button == 4) {
// right mousebutton, wheel down -> rotate inventory
rotate_inventory(+1);
}
else if (e.button.button == 5) {
// wheel down -> inverse rotate inventory
rotate_inventory(-1);
}
}
update_mouse_button_state();
}
void Client::rotate_inventory (int direction)
{
m_user_input = "";
STATUSBAR->hide_text();
player::RotateInventory(direction);
}
/* -------------------- Console related -------------------- */
class HistoryProxy {
static int instances;
public:
static string content;
HistoryProxy();
~HistoryProxy() {
if (!--instances) app.state->setProperty("CommandHistory", content);
}
};
string HistoryProxy::content;
int HistoryProxy::instances = 0;
HistoryProxy::HistoryProxy() {
if (!instances++) {
content = app.state->getString("CommandHistory");
if (content.find(HSEP) == string::npos) content = string(1, HSEP);
}
}
static void user_input_history_append(const string& text, bool at_end = true) {
HistoryProxy history;
size_t old_pos = history.content.find(string(1, HSEP)+text+HSEP);
if (old_pos != string::npos)
history.content.erase(old_pos, text.length()+1);
if (at_end)
history.content += text+HSEP;
else
history.content = string(1, HSEP)+text+history.content;
}
void Client::process_userinput()
{
if (m_user_input != "") {
STATUSBAR->hide_text();
string commands = m_user_input;
user_input_history_append(m_user_input);
m_user_input = "";
size_t sep_pos;
while ((sep_pos = commands.find_first_of(';')) != string::npos) {
string first_command = commands.substr(0, sep_pos);
commands.erase(0, sep_pos+1);
server::Msg_Command (first_command);
}
server::Msg_Command (commands); // last command
}
}
void Client::user_input_append (char c) {
m_user_input += c;
Msg_ShowText (m_user_input, false);
}
void Client::user_input_backspace ()
{
if (!m_user_input.empty()) {
m_user_input.erase (m_user_input.size()-1, 1);
if (!m_user_input.empty()) {
// still not empty
Msg_ShowText (m_user_input, false);
} else {
// empty
STATUSBAR->hide_text();
}
}
}
void Client::user_input_previous ()
{
HistoryProxy history;
size_t last_start = history.content.find_last_of(HSEP, history.content.length()-2);
if (last_start != string::npos) {
string prev_input = history.content.substr(last_start+1, history.content.length()-last_start-2);
history.content.erase(last_start+1);
user_input_history_append(m_user_input, false);
m_user_input = prev_input;
if (m_user_input.empty())
STATUSBAR->hide_text();
else
Msg_ShowText (m_user_input, false);
}
}
void Client::user_input_next ()
{
HistoryProxy history;
size_t first_end = history.content.find_first_of(HSEP, 1);
if (first_end != string::npos) {
string next_input = history.content.substr(1, first_end-1);
history.content.erase(0, first_end);
user_input_history_append(m_user_input);
m_user_input = next_input;
if (m_user_input.empty())
STATUSBAR->hide_text();
else
Msg_ShowText (m_user_input, false);
}
}
void Client::on_keydown(SDL_Event &e)
{
SDLKey keysym = e.key.keysym.sym;
SDLMod keymod = e.key.keysym.mod;
if (keymod & KMOD_CTRL) {
switch (keysym) {
case SDLK_a:
server::Msg_Command ("restart");
break;
case SDLK_F3:
if (keymod & KMOD_SHIFT) {
// force a reload from file
lev::Proxy * curProxy = lev::Proxy::loadedLevel();
if (curProxy != NULL)
curProxy->release();
server::Msg_Command ("restart");
}
default:
break;
};
}
else if (keymod & KMOD_ALT) {
switch (keysym) {
case SDLK_x: abort(); break;
case SDLK_t:
if (enigma::WizardMode) {
Screen *scr = video::GetScreen();
ecl::TintRect(scr->get_surface (), display::GetGameArea(),
100, 100, 100, 0);
scr->update_all();
}
break;
case SDLK_s:
if (enigma::WizardMode) {
server::Msg_Command ("god");
}
break;
case SDLK_RETURN:
{
video::TempInputGrab (false);
video::ToggleFullscreen ();
sdl::FlushEvents();
}
break;
default:
break;
};
}
else {
switch (keysym) {
case SDLK_ESCAPE: show_menu(); break;
case SDLK_LEFT: set_mousespeed(options::GetMouseSpeed() - 1); break;
case SDLK_RIGHT: set_mousespeed(options::GetMouseSpeed() + 1); break;
#ifdef ANDROID
case SDLK_LCTRL:
#endif
case SDLK_TAB: rotate_inventory(+1); break;
case SDLK_F1: show_help(); break;
case SDLK_F2:
// display hint
break;
case SDLK_F3:
if (keymod & KMOD_SHIFT)
server::Msg_Command ("restart");
else
server::Msg_Command ("suicide");
break;
case SDLK_F4: Msg_AdvanceLevel(lev::ADVANCE_STRICTLY); break;
case SDLK_F5: Msg_AdvanceLevel(lev::ADVANCE_UNSOLVED); break;
case SDLK_F6: Msg_JumpBack(); break;
case SDLK_F10: {
lev::Proxy *level = lev::Proxy::loadedLevel();
std::string basename = std::string("screenshots/") +
level->getLocalSubstitutionLevelPath();
std::string fname = basename + ".png";
std::string fullPath;
int i = 1;
while (app.resourceFS->findFile(fname, fullPath)) {
fname = basename + ecl::strf("#%d", i++) + ".png";
}
std::string savePath = app.userImagePath + "/" + fname;
video::Screenshot(savePath);
break;
}
case SDLK_RETURN: process_userinput(); break;
case SDLK_BACKSPACE: user_input_backspace(); break;
case SDLK_UP: user_input_previous(); break;
case SDLK_DOWN: user_input_next(); break;
default:
if (e.key.keysym.unicode && (e.key.keysym.unicode & 0xff80) == 0) {
char ascii = static_cast<char>(e.key.keysym.unicode & 0x7f);
if (isalnum (ascii) ||
strchr(" .-!\"$%&/()=?{[]}\\#'+*~_,;.:<>|", ascii)) // don't add '^' or change history code
{
user_input_append(ascii);
}
}
break;
}
}
}
static const char *helptext_ingame[] = {
N_("Left mouse button:"), N_("Activate/drop leftmost inventory item"),
N_("Right mouse button:"), N_("Rotate inventory items"),
N_("Escape:"), N_("Show game menu"),
N_("F1:"), N_("Show this help"),
N_("F3:"), N_("Kill current marble"),
N_("Shift+F3:"), N_("Restart the current level"),
N_("F4:"), N_("Skip to next level"),
N_("F5:"), 0, // see below
N_("F6:"), N_("Jump back to last level"),
N_("F10:"), N_("Make screenshot"),
N_("Left/right arrow:"), N_("Change mouse speed"),
N_("Alt+x:"), N_("Return to level menu"),
// N_("Alt+Return:"), N_("Switch between fullscreen and window"),
0
};
void Client::show_help()
{
server::Msg_Pause (true);
video::TempInputGrab grab(false);
helptext_ingame[15] = app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST
? _("Skip to next level for best score hunt")
: _("Skip to next unsolved level");
video::ShowMouse();
gui::displayHelp(helptext_ingame, 200);
video::HideMouse();
update_mouse_button_state();
if (m_state == cls_game)
display::RedrawAll(video::GetScreen());
server::Msg_Pause (false);
game::ResetGameTimer();
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST)
server::Msg_Command ("restart"); // inhibit cheating
}
void Client::show_menu()
{
server::Msg_Pause (true);
ecl::Screen *screen = video::GetScreen();
video::TempInputGrab grab (false);
video::ShowMouse();
{
int x, y;
display::GetReferencePointCoordinates(&x, &y);
enigma::gui::GameMenu(x, y).manage();
}
video::HideMouse();
update_mouse_button_state();
if (m_state == cls_game)
display::RedrawAll(screen);
server::Msg_Pause (false);
game::ResetGameTimer();
}
void Client::draw_screen()
{
switch (m_state) {
case cls_error: {
Screen *scr = video::GetScreen();
GC gc (scr->get_surface());
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont("menufont");
vector<string> lines;
ecl::split_copy (m_error_message, '\n', back_inserter(lines));
int x = 60;
int y = 60;
int yskip = 25;
const video::VMInfo *vminfo = video::GetInfo();
int width = vminfo->width - 120;
for (unsigned i=0; i<lines.size(); ) {
std::string::size_type breakPos = ecl::breakString (f, lines[i],
" ", width);
f->render(gc, x, y, lines[i].substr(0,breakPos).c_str());
y += yskip;
if (breakPos != lines[i].size()) {
// process rest of line
lines[i] = lines[i].substr(breakPos);
} else {
// process next line
i++;
}
}
scr->update_all();
scr->flush_updates();
break;
}
default:
break;
}
}
std::string Client::init_hunted_time()
{
std::string hunted;
m_hunt_against_time = 0;
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) {
lev::Index *ind = lev::Index::getCurrentIndex();
lev::ScoreManager *scm = lev::ScoreManager::instance();
lev::Proxy *curProxy = ind->getCurrent();
lev::RatingManager *ratingMgr = lev::RatingManager::instance();
int difficulty = app.state->getInt("Difficulty");
int wr_time = ratingMgr->getBestScore(curProxy, difficulty);
int best_user_time = scm->getBestUserScore(curProxy, difficulty);
if (best_user_time>0 && (wr_time == -1 || best_user_time<wr_time)) {
m_hunt_against_time = best_user_time;
hunted = "you";
}
else if (wr_time>0) {
m_hunt_against_time = wr_time;
hunted = ratingMgr->getBestScoreHolder(curProxy, difficulty);
}
// STATUSBAR->set_timerstart(-m_hunt_against_time);
}
return hunted;
}
void Client::tick (double dtime)
{
const double timestep = 0.01; // 10ms
#ifdef ANDROID
Sint32 joy_x, joy_y;
SDL_Joystick *joy;
#endif
switch (m_state) {
case cls_idle:
break;
case cls_preparing_game: {
#ifdef ANDROID
// calibrate the orientation sensor, using the current position as zero
// TODO: average the values over some period of time?
joy = SDL_JoystickOpen(0);
SDL_JoystickUpdate();
if(joy != NULL) {
m_joy_x0 = SDL_JoystickGetAxis(joy,0);
m_joy_y0 = SDL_JoystickGetAxis(joy,1);
}
#endif
video::TransitionEffect *fx = m_effect.get();
if (fx && !fx->finished()) {
fx->tick (dtime);
}
else {
m_effect.reset();
server::Msg_StartGame();
m_state = cls_game;
m_timeaccu = 0;
m_total_game_time = 0;
sdl::FlushEvents();
}
break;
}
case cls_game:
#ifdef ANDROID
// joystick/accelerometer control
joy = SDL_JoystickOpen(0);
SDL_JoystickUpdate();
if(joy != NULL) {
joy_x = SDL_JoystickGetAxis(joy,0) - m_joy_x0;
joy_y = SDL_JoystickGetAxis(joy,1) - m_joy_y0;
server::Msg_MouseForce(options::GetDouble("MouseSpeed") * -dtime/3000.0 *
V2 (joy_x*sqrt(abs(joy_x)), joy_y*sqrt(abs(joy_y)))); // use joy**1.5 to allow more flexible (non-linear) control
}
#endif
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) {
int old_second = round_nearest<int> (m_total_game_time);
int second = round_nearest<int> (m_total_game_time + dtime);
if (m_hunt_against_time && old_second <= m_hunt_against_time) {
if (second > m_hunt_against_time) { // happens exactly once when par has passed by
lev::Index *ind = lev::Index::getCurrentIndex();
lev::ScoreManager *scm = lev::ScoreManager::instance();
lev::Proxy *curProxy = ind->getCurrent();
lev::RatingManager *ratingMgr = lev::RatingManager::instance();
int difficulty = app.state->getInt("Difficulty");
int wr_time = ratingMgr->getBestScore(curProxy, difficulty);
int best_user_time = scm->getBestUserScore(curProxy, difficulty);
string message;
if (wr_time>0 && (best_user_time<0 || best_user_time>wr_time)) {
message = string(_("Too slow for ")) +
ratingMgr->getBestScoreHolder(curProxy, difficulty) +
".. [Ctrl-A]";
}
else {
message = string(_("You are slow today.. [Ctrl-A]"));
}
client::Msg_PlaySound("shatter", 1.0);
Msg_ShowText(message, true, 2.0);
}
else {
if (old_second<second && // tick every second
(second >= (m_hunt_against_time-5) || // at least 5 seconds
second >= round_nearest<int> (m_hunt_against_time*.8))) // or the last 20% before par
{
client::Msg_PlaySound("pickup", 1.0);
}
}
}
}
m_total_game_time += dtime;
STATUSBAR->set_time (m_total_game_time);
// fall through
case cls_finished: {
m_timeaccu += dtime;
for (;m_timeaccu >= timestep; m_timeaccu -= timestep) {
display::Tick (timestep);
}
display::Redraw(video::GetScreen());
handle_events();
break;
}
case cls_gamemenu:
break;
case cls_gamehelp:
break;
case cls_abort:
break;
case cls_error:
{
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_KEYDOWN:
case SDL_QUIT:
client::Msg_Command("abort");
break;
}
}
}
break;
}
}
void Client::level_finished()
{
lev::Index *ind = lev::Index::getCurrentIndex();
lev::ScoreManager *scm = lev::ScoreManager::instance();
lev::Proxy *curProxy = ind->getCurrent();
lev::RatingManager *ratingMgr = lev::RatingManager::instance();
int difficulty = app.state->getInt("Difficulty");
int wr_time = ratingMgr->getBestScore(curProxy, difficulty);
int best_user_time = scm->getBestUserScore(curProxy, difficulty);
string par_name = ratingMgr->getBestScoreHolder(curProxy, difficulty);
int par_time = ratingMgr->getParScore(curProxy, difficulty);
int level_time = round_nearest<int> (m_total_game_time);
string text;
bool timehunt_restart = false;
if (wr_time > 0) {
if (best_user_time<0 || best_user_time>wr_time) {
if (level_time == wr_time)
text = string(_("Exactly the world record of "))+par_name+"!";
else if (level_time<wr_time)
text = _("Great! A new world record!");
}
}
if (text.length() == 0 && best_user_time>0) {
if (level_time == best_user_time) {
text = _("Again your personal record...");
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST)
timehunt_restart = true; // when hunting yourself: Equal is too slow
}
else if (level_time<best_user_time)
if (par_time >= 0 && level_time <= par_time)
text = _("New personal record - better than par!");
else if (par_time >= 0)
text = _("New personal record, but over par!");
else
text = _("New personal record!");
}
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST &&
(wr_time>0 || best_user_time>0))
{
bool with_par = best_user_time == -1 || (wr_time >0 && wr_time<best_user_time);
int behind = level_time - (with_par ? wr_time : best_user_time);
if (behind>0) {
if (best_user_time>0 && level_time<best_user_time && with_par) {
text = _("Your record, ");
}
else {
text = "";
}
text += strf("%d:%02d ", static_cast<int> (behind/60)%100, behind%60);
if (with_par)
text += _("behind world record.");
else
text += _("behind your record.");
timehunt_restart = true; // time hunt failed -> repeat level
}
}
if (text.length() == 0) {
if (par_time >= 0 && level_time <= par_time)
text = _("Level finished - better than par!");
else if (par_time >= 0)
text = _("Level finished, but over par!");
else
text = _("Level finished!");
}
if (m_cheater)
text += _(" Cheater!");
Msg_ShowText (text, false);
if (!m_cheater) {
scm->updateUserScore(curProxy, difficulty, level_time);
// save score (just in case Enigma crashes when loading next level)
lev::ScoreManager::instance()->save();
}
if (timehunt_restart)
server::Msg_Command("restart");
else
m_state = cls_finished;
}
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
void Client::level_loaded(bool isRestart)
{
lev::Index *ind = lev::Index::getCurrentIndex();
lev::ScoreManager *scm = lev::ScoreManager::instance();
lev::Proxy *curProxy = ind->getCurrent();
// update window title
video::SetCaption(ecl::strf(_("Enigma pack %s - level #%d: %s"), ind->getName().c_str(),
ind->getCurrentLevel(), curProxy->getTitle().c_str()).c_str());
string hunted = init_hunted_time(); // sets m_hunt_against_time (used below)
// show level information (name, author, etc.)
{
string displayed_info = "";
if (m_hunt_against_time>0) {
if (hunted == "you")
displayed_info = _("Your record: ");
else
displayed_info = _("World record to beat: ");
displayed_info += ecl::strf("%d:%02d", (m_hunt_against_time/60)%100,
m_hunt_against_time%60);
//+ _(" by ") +hunted;
// makes the string too long in many levels
Msg_ShowText (displayed_info, true, 4.0);
}
else {
displayed_info = displayedLevelInfo(curProxy);
Msg_ShowText (displayed_info, true, 2.0);
}
}
sound::FadeoutMusic();
if (options::GetBool("InGameMusic")) {
sound::PlayMusic (options::GetString("LevelMusicFile"));
} else {
sound::StopMusic();
}
// start screen transition
GC gc(video::BackBuffer());
display::DrawAll(gc);
m_effect.reset (video::MakeEffect ((isRestart ? video::TM_SQUARES :
video::TM_PUSH_RANDOM), video::BackBuffer()));
m_cheater = false;
m_state = cls_preparing_game;
}
void Client::handle_message (Message *m) { // @@@ unused
switch (m->type) {
case CLMSG_LEVEL_LOADED:
break;
default:
fprintf (stderr, "Unhandled client event: %d\n", m->type);
break;
}
}
void Client::error (const string &text)
{
m_error_message = text;
m_state = cls_error;
draw_screen();
}
/* -------------------- Functions -------------------- */
bool client::NetworkStart()
{
return CLIENT.network_start();
}
void client::Msg_LevelLoaded(bool isRestart)
{
CLIENT.level_loaded(isRestart);
}
void client::Tick (double dtime) {
CLIENT.tick (dtime);
sound::Tick (dtime);
}
void client::Stop() {
CLIENT.stop ();
}
void client::Msg_AdvanceLevel (lev::LevelAdvanceMode mode) {
lev::Index *ind = lev::Index::getCurrentIndex();
// log last played level
lev::PersistentIndex::addCurrentToHistory();
if (ind->advanceLevel(mode)) {
// now we may advance
server::Msg_LoadLevel(ind->getCurrent(), false);
}
else
client::Msg_Command("abort");
}
void client::Msg_JumpBack() {
// log last played level
lev::PersistentIndex::addCurrentToHistory();
server::Msg_JumpBack();
}
bool client::AbortGameP() {
return CLIENT.abort_p();
}
void client::Msg_Command(const string& cmd) {
if (cmd == "abort") {
CLIENT.abort();
}
else if (cmd == "level_finished") {
client::Msg_PlaySound("finished", 1.0);
CLIENT.level_finished();
}
else if (cmd == "cheater") {
CLIENT.mark_cheater();
}
else if (cmd == "easy_going") {
CLIENT.easy_going();
}
else {
enigma::Log << "Warning: Client received unknown command '" << cmd << "'\n";
}
}
void client::Msg_PlayerPosition (unsigned iplayer, const V2 &pos)
{
if (iplayer == (unsigned)player::CurrentPlayer()) {
sound::SetListenerPosition (pos);
display::SetReferencePoint (pos);
}
}
void client::Msg_PlaySound (const std::string &wavfile,
const ecl::V2 &pos,
double relative_volume)
{
sound::EmitSoundEvent (wavfile.c_str(), pos, relative_volume);
}
void client::Msg_PlaySound (const std::string &wavfile, double relative_volume)
{
sound::EmitSoundEvent (wavfile.c_str(), V2(), relative_volume);
}
void client::Msg_Sparkle (const ecl::V2 &pos) {
display::AddEffect (pos, "ring-anim");
}
void client::Msg_ShowText
(const std::string &text, bool scrolling, double duration)
{
STATUSBAR->show_text (text, scrolling, duration);
}
void client::Msg_Error (const std::string &text)
{
CLIENT.error (text);
}