scummvm/saga/interface.cpp

587 lines
14 KiB
C++
Raw Normal View History

/* ScummVM - Scumm Interpreter
* Copyright (C) 2004 The ScummVM project
*
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
*
* 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.
*
* $Header$
*
*/
2004-05-01 09:37:24 +00:00
// Game interface module
2004-08-02 16:20:35 +00:00
#include "saga/saga.h"
2004-08-02 16:20:35 +00:00
#include "saga/gfx.h"
#include "saga/game_mod.h"
#include "saga/cvar_mod.h"
#include "saga/actor.h"
2004-08-10 18:31:33 +00:00
#include "saga/console.h"
2004-08-03 00:06:18 +00:00
#include "saga/font.h"
2004-08-02 16:20:35 +00:00
#include "saga/objectmap.h"
#include "saga/rscfile_mod.h"
#include "saga/script.h"
2004-08-03 01:07:34 +00:00
#include "saga/sprite.h"
2004-08-02 16:20:35 +00:00
#include "saga/interface.h"
#include "saga/sdata.h"
namespace Saga {
static R_VERB_DATA I_VerbData[] = {
{I_VERB_WALKTO, "verb_walkto", "Walk to", S_VERB_WALKTO},
{I_VERB_LOOKAT, "verb_lookat", "Look at", S_VERB_LOOKAT},
{I_VERB_PICKUP, "verb_pickup", "Pick up", S_VERB_PICKUP},
{I_VERB_TALKTO, "verb_talkto", "Talk to", S_VERB_TALKTO},
{I_VERB_OPEN, "verb_open", "Open", S_VERB_OPEN},
{I_VERB_CLOSE, "verb_close", "Close", S_VERB_CLOSE},
{I_VERB_USE, "verb_use", "Use", S_VERB_USE},
{I_VERB_GIVE, "verb_give", "Give", S_VERB_GIVE}
};
static R_INTERFACE_DESC ITE_interface = {
ITE_STATUS_Y,
ITE_STATUS_W,
ITE_STATUS_H,
ITE_STATUS_TEXT_Y,
ITE_STATUS_TXTCOL,
ITE_STATUS_BGCOL,
ITE_CMD_TEXT_COL,
ITE_CMD_TEXT_SHADOWCOL,
ITE_CMD_TEXT_HILITECOL,
COMMAND_DEFAULT_BUTTON,
ITE_LPORTRAIT_X,
ITE_LPORTRAIT_Y
};
static R_INTERFACE_BUTTON ITE_c_buttons[] = {
{5, 4, 46, 47, "Portrait", 0, 0, BUTTON_NONE, 0},
2004-05-01 09:37:24 +00:00
// "Walk To" and "Talk To" share button sprites
{52, 4, 109, 14, "Walk To", 1, 2, BUTTON_VERB, I_VERB_WALKTO},
{52, 15, 109, 25, "Look At", 3, 4, BUTTON_VERB, I_VERB_LOOKAT},
{52, 26, 109, 36, "Pick Up", 5, 6, BUTTON_VERB, I_VERB_PICKUP},
{52, 37, 109, 47, "Talk To", 1, 2, BUTTON_VERB, I_VERB_TALKTO},
{110, 4, 166, 14, "Open", 7, 8, BUTTON_VERB, I_VERB_OPEN},
{110, 15, 166, 25, "Close", 9, 10, BUTTON_VERB, I_VERB_CLOSE},
{110, 26, 166, 36, "Use", 11, 12, BUTTON_VERB, I_VERB_USE},
{110, 37, 166, 47, "Give", 13, 14, BUTTON_VERB, I_VERB_GIVE},
{181, 6, 206, 24, "Inv1", 0, 0, BUTTON_NONE, 0},
{213, 6, 240, 24, "Inv2", 0, 0, BUTTON_NONE, 0},
{245, 6, 272, 24, "Inv3", 0, 0, BUTTON_NONE, 0},
{277, 6, 304, 24, "Inv4", 0, 0, BUTTON_NONE, 0},
{181, 27, 208, 45, "Inv5", 0, 0, BUTTON_NONE, 0},
{213, 27, 240, 45, "Inv6", 0, 0, BUTTON_NONE, 0},
{245, 27, 272, 45, "Inv7", 0, 0, BUTTON_NONE, 0},
{277, 27, 304, 45, "Inv8", 0, 0, BUTTON_NONE, 0},
{306, 6, 314, 11, "InvUp", 0, 0, BUTTON_NONE, 0},
{306, 41, 314, 45, "InvDown", 0, 0, BUTTON_NONE, 0}
};
static R_INTERFACE_DESC IHNM_interface = {
IHNM_STATUS_Y,
IHNM_STATUS_W,
IHNM_STATUS_H,
IHNM_STATUS_TEXT_Y,
IHNM_STATUS_TXTCOL,
IHNM_STATUS_BGCOL,
IHNM_CMD_TEXT_COL,
IHNM_CMD_TEXT_SHADOWCOL,
IHNM_CMD_TEXT_HILITECOL,
COMMAND_DEFAULT_BUTTON,
IHNM_LPORTRAIT_X,
IHNM_LPORTRAIT_Y
};
static R_INTERFACE_BUTTON IHNM_c_buttons[] = {
{5, 4, 46, 47, "Portrait", 0, 0, 0, 0}
};
int Interface::registerLang(void) {
size_t i;
2004-04-25 15:14:46 +00:00
for (i = 0; i < ARRAYSIZE(I_VerbData); i++) {
if (CVAR_Register_S(I_VerbData[i].verb_str,
I_VerbData[i].verb_cvar,
NULL, R_CVAR_CFG, R_VERB_STRLIMIT) != R_SUCCESS) {
return R_FAILURE;
}
assert(CVAR_Find(I_VerbData[i].verb_cvar) != NULL);
}
return R_SUCCESS;
}
Interface::Interface(SagaEngine *vm) : _vm(vm), _initialized(false) {
R_GAME_RESOURCEDESC g_resdesc;
int game_type;
int result;
if (_initialized) {
return;
}
_iThread = _vm->_script->SThreadCreate();
if (_iThread == NULL) {
warning("Interface::Interface(): Error creating script thread for game interface module");
return;
}
2004-05-01 09:37:24 +00:00
// Load interface module resource file context
result = GAME_GetFileContext(&_interfaceContext, R_GAME_RESOURCEFILE, 0);
if (result != R_SUCCESS) {
return;
}
2004-05-01 09:37:24 +00:00
// Initialize interface data by game type
game_type = GAME_GetGameType();
if (game_type == GID_ITE) {
2004-05-01 09:37:24 +00:00
// Load Inherit the Earth interface desc
_cPanel.buttons = ITE_c_buttons;
_cPanel.nbuttons = ARRAYSIZE(ITE_c_buttons);
_iDesc = ITE_interface;
} else if (game_type == GID_IHNM) {
2004-05-01 09:37:24 +00:00
// Load I Have No Mouth interface desc
_cPanel.buttons = IHNM_c_buttons;
_cPanel.nbuttons = ARRAYSIZE(IHNM_c_buttons);
_iDesc = IHNM_interface;
} else {
return;
}
2004-05-01 09:37:24 +00:00
// Load interface resources
GAME_GetResourceInfo(&g_resdesc);
2004-05-01 09:37:24 +00:00
// Load command panel resource
result = RSC_LoadResource(_interfaceContext, g_resdesc.command_panel_rn,
&_cPanel.res, &_cPanel.res_len);
if (result != R_SUCCESS) {
return;
}
2004-05-01 09:37:24 +00:00
// Load dialogue panel resource
result = RSC_LoadResource(_interfaceContext, g_resdesc.dialogue_panel_rn,
&_dPanel.res, &_dPanel.res_len);
if (result != R_SUCCESS) {
return;
}
_vm->_sprite->loadList(ITE_COMMAND_BUTTONSPRITES, &_cPanel.sprites);
_vm->_sprite->loadList(ITE_DEFAULT_PORTRAITS, &_defPortraits);
_vm->decodeBGImage(_cPanel.res, _cPanel.res_len, &_cPanel.img,
&_cPanel.img_len, &_cPanel.img_w, &_cPanel.img_h);
_vm->decodeBGImage(_dPanel.res, _dPanel.res_len,
&_dPanel.img, &_dPanel.img_len,
&_dPanel.img_w, &_dPanel.img_h);
_cPanel.x = 0;
_cPanel.y = 149;
_dPanel.x = 0;
_dPanel.y = 149;
_cPanel.set_button = COMMAND_DEFAULT_BUTTON;
_activePortrait = 0;
_activeVerb = I_VERB_WALKTO;
_active = 0;
_panelMode = PANEL_COMMAND;
*_statusText = 0;
_initialized = true;
}
Interface::~Interface(void) {
_initialized = false;
}
int Interface::activate() {
_active = 1;
draw();
return R_SUCCESS;
}
int Interface::deactivate() {
_active = 0;
return R_SUCCESS;
}
int Interface::setStatusText(const char *new_txt) {
assert(new_txt != NULL);
strncpy(_statusText, new_txt, R_STATUS_TEXT_LEN);
return R_SUCCESS;
}
int Interface::draw() {
R_GAME_DISPLAYINFO g_di;
R_SURFACE *back_buf;
int xbase;
int ybase;
int lportrait_x;
int lportrait_y;
R_RECT rect;
R_POINT origin;
back_buf = _vm->_gfx->getBackBuffer();
if (!_active) {
return R_SUCCESS;
}
2004-05-01 09:37:24 +00:00
// Get game display info
GAME_GetDisplayInfo(&g_di);
2004-05-01 09:37:24 +00:00
// Erase background of status bar
2004-04-25 15:14:46 +00:00
rect.left = 0;
rect.top = _iDesc.status_h - 1;
2004-04-25 15:14:46 +00:00
rect.right = g_di.logical_w - 1;
rect.bottom = _iDesc.status_y;
_vm->_gfx->drawRect(back_buf, &rect, _iDesc.status_bgcol);
2004-05-01 09:37:24 +00:00
// Draw command panel background
if (_panelMode == PANEL_COMMAND) {
xbase = _cPanel.x;
ybase = _cPanel.y;
origin.x = 0;
origin.y = g_di.logical_h - _cPanel.img_h;
_vm->_gfx->bufToSurface(back_buf, _cPanel.img, _cPanel.img_w,
_cPanel.img_h, NULL, &origin);
} else {
xbase = _dPanel.x;
ybase = _dPanel.y;
origin.x = 0;
origin.y = g_di.logical_h - _cPanel.img_h;
_vm->_gfx->bufToSurface(back_buf, _dPanel.img, _dPanel.img_w,
_dPanel.img_h, NULL, &origin);
}
2004-05-01 09:37:24 +00:00
// Draw character portrait
lportrait_x = xbase + _iDesc.lportrait_x;
lportrait_y = ybase + _iDesc.lportrait_y;
_vm->_sprite->draw(back_buf, _defPortraits, _activePortrait, lportrait_x, lportrait_y);
return R_SUCCESS;
}
int Interface::update(R_POINT *imouse_pt, int update_flag) {
R_GAME_DISPLAYINFO g_di;
R_SURFACE *back_buf;
int imouse_x, imouse_y;
assert(imouse_pt != NULL);
if (!_active) {
return R_SUCCESS;
}
imouse_x = imouse_pt->x;
imouse_y = imouse_pt->y;
back_buf = _vm->_gfx->getBackBuffer();
2004-05-01 09:37:24 +00:00
// Get game display info
GAME_GetDisplayInfo(&g_di);
2004-05-01 09:37:24 +00:00
// Update playfield space ( only if cursor is inside )
if (imouse_y < g_di.scene_h) {
2004-05-01 09:37:24 +00:00
// Mouse is in playfield space
if (update_flag == UPDATE_MOUSEMOVE) {
handlePlayfieldUpdate(back_buf, imouse_pt);
} else if (update_flag == UPDATE_MOUSECLICK) {
handlePlayfieldClick(back_buf, imouse_pt);
}
}
2004-05-01 09:37:24 +00:00
// Update command space
if (update_flag == UPDATE_MOUSEMOVE) {
handleCommandUpdate(back_buf, imouse_pt);
} else if (update_flag == UPDATE_MOUSECLICK) {
handleCommandClick(back_buf, imouse_pt);
}
drawStatusBar(back_buf);
return R_SUCCESS;
}
int Interface::drawStatusBar(R_SURFACE *ds) {
R_GAME_DISPLAYINFO g_di;
R_RECT rect;
int string_w;
2004-05-01 09:37:24 +00:00
// Get game display info
GAME_GetDisplayInfo(&g_di);
2004-05-01 09:37:24 +00:00
// Erase background of status bar
2004-04-25 15:14:46 +00:00
rect.left = 0;
rect.top = _iDesc.status_y;
2004-04-25 15:14:46 +00:00
rect.right = g_di.logical_w - 1;
rect.bottom = _iDesc.status_y + _iDesc.status_h - 1;
_vm->_gfx->drawRect(ds, &rect, _iDesc.status_bgcol);
string_w = _vm->_font->getStringWidth(SMALL_FONT_ID, _statusText, 0, 0);
_vm->_font->draw(SMALL_FONT_ID, ds, _statusText, 0, (_iDesc.status_w / 2) - (string_w / 2),
_iDesc.status_y + _iDesc.status_txt_y, _iDesc.status_txt_col, 0, 0);
return R_SUCCESS;
}
int Interface::handleCommandClick(R_SURFACE *ds, R_POINT *imouse_pt) {
int hit_button;
int ibutton_num;
int x_base;
int y_base;
int button_x = 0;
int button_y = 0;
int old_set_button;
int set_button;
hit_button = hitTest(imouse_pt, &ibutton_num);
if (hit_button != R_SUCCESS) {
2004-05-01 09:37:24 +00:00
// Clicking somewhere other than a button doesn't do anything
return R_SUCCESS;
}
x_base = _cPanel.x;
y_base = _cPanel.y;
if (_cPanel.buttons[ibutton_num].flags & BUTTON_SET) {
old_set_button = _cPanel.set_button;
set_button = ibutton_num;
_cPanel.set_button = set_button;
if (_cPanel.buttons[set_button].flags & BUTTON_VERB) {
_activeVerb = _cPanel.buttons[ibutton_num].data;
}
if (_cPanel.buttons[set_button].flags & BUTTON_BITMAP) {
button_x = x_base + _cPanel.buttons[set_button].x1;
button_y = y_base + _cPanel.buttons[set_button].y1;
_vm->_sprite->draw(ds, _cPanel.sprites, _cPanel.buttons[set_button].
2004-05-01 09:37:24 +00:00
active_sprite - 1, button_x, button_y);
}
if (_cPanel.buttons[old_set_button].flags & BUTTON_BITMAP) {
button_x = x_base + _cPanel.buttons[old_set_button].x1;
button_y = y_base + _cPanel.buttons[old_set_button].y1;
_vm->_sprite->draw(ds, _cPanel.sprites, _cPanel.buttons[old_set_button].
2004-05-01 09:37:24 +00:00
inactive_sprite - 1, button_x, button_y);
}
}
return R_SUCCESS;
}
int Interface::handleCommandUpdate(R_SURFACE *ds, R_POINT *imouse_pt) {
int hit_button;
int ibutton_num;
int button_x = 0;
int button_y = 0;
int button_w = 0;
int verb_idx = 0;
int string_w = 0;
int color;
int i;
hit_button = hitTest(imouse_pt, &ibutton_num);
if (hit_button == R_SUCCESS) {
2004-05-01 09:37:24 +00:00
// Hovering over a command panel button
setStatusText(I_VerbData[_activeVerb].verb_str);
}
for (i = 0; i < _cPanel.nbuttons; i++) {
if (!(_cPanel.buttons[i].flags & BUTTON_LABEL)) {
continue;
}
button_w = _cPanel.buttons[i].x2 - _cPanel.buttons[i].x1;
verb_idx = _cPanel.buttons[i].data;
2004-08-03 00:06:18 +00:00
string_w = _vm->_font->getStringWidth(SMALL_FONT_ID, I_VerbData[verb_idx].verb_str, 0, 0);
if (i == hit_button) {
color = _iDesc.cmd_txt_hilitecol;
} else {
color = _iDesc.cmd_txt_col;
}
button_x = _cPanel.x + _cPanel.buttons[i].x1;
button_y = _cPanel.y + _cPanel.buttons[i].y1;
2004-08-03 00:06:18 +00:00
_vm->_font->draw(SMALL_FONT_ID, ds, I_VerbData[verb_idx].verb_str, 0,
2004-05-01 09:37:24 +00:00
button_x + ((button_w / 2) - (string_w / 2)), button_y + 1,
color, _iDesc.cmd_txt_shadowcol, FONT_SHADOW);
2004-05-01 09:37:24 +00:00
if ((i == _cPanel.set_button) && (_cPanel.buttons[i].flags & BUTTON_BITMAP)) {
_vm->_sprite->draw(ds, _cPanel.sprites, _cPanel.buttons[i].active_sprite - 1,
2004-05-01 09:37:24 +00:00
button_x, button_y);
}
}
return R_SUCCESS;
}
int Interface::handlePlayfieldClick(R_SURFACE *ds, R_POINT *imouse_pt) {
int hit_object;
int object_num;
uint16 object_flags = 0;
int script_num;
R_POINT iactor_pt;
2004-08-02 15:44:18 +00:00
hit_object = _vm->_objectMap->hitTest(imouse_pt, &object_num);
if (hit_object != R_SUCCESS) {
2004-05-01 09:37:24 +00:00
// Player clicked on empty spot - walk here regardless of verb
2004-08-02 15:47:42 +00:00
_vm->_actor->StoA(&iactor_pt, imouse_pt);
_vm->_actor->walkTo(0, &iactor_pt, 0, NULL);
return R_SUCCESS;
}
2004-08-02 15:44:18 +00:00
if (_vm->_objectMap->getFlags(object_num, &object_flags) != R_SUCCESS) {
2004-08-10 18:31:33 +00:00
_vm->_console->print("Invalid object number: %d\n", object_num);
return R_FAILURE;
}
if (object_flags & R_OBJECT_NORMAL) {
2004-08-02 15:44:18 +00:00
if (_vm->_objectMap->getEPNum(object_num, &script_num) == R_SUCCESS) {
2004-05-01 09:37:24 +00:00
// Set active verb in script module
_vm->_sdata->putWord(4, 4, I_VerbData[_activeVerb].s_verb);
2004-05-01 09:37:24 +00:00
// Execute object script if present
if (script_num != 0) {
_vm->_script->SThreadExecute(_iThread, script_num);
}
}
} else {
2004-05-01 09:37:24 +00:00
// Not a normal scene object - walk to it as if it weren't there
2004-08-02 15:47:42 +00:00
_vm->_actor->StoA(&iactor_pt, imouse_pt);
_vm->_actor->walkTo(0, &iactor_pt, 0, NULL);
}
return R_SUCCESS;
}
int Interface::handlePlayfieldUpdate(R_SURFACE *ds, R_POINT *imouse_pt) {
const char *object_name;
int object_num;
uint16 object_flags = 0;
char new_status[R_STATUS_TEXT_LEN];
int hit_object;
new_status[0] = 0;
2004-08-02 15:44:18 +00:00
hit_object = _vm->_objectMap->hitTest(imouse_pt, &object_num);
if (hit_object != R_SUCCESS) {
2004-05-01 09:37:24 +00:00
// Cursor over nothing - just display current verb
setStatusText(I_VerbData[_activeVerb].verb_str);
return R_SUCCESS;
}
2004-08-02 15:44:18 +00:00
if (_vm->_objectMap->getFlags(object_num, &object_flags) != R_SUCCESS) {
2004-08-10 18:31:33 +00:00
_vm->_console->print("Invalid object number: %d\n", object_num);
return R_FAILURE;
}
2004-08-02 15:44:18 +00:00
_vm->_objectMap->getName(object_num, &object_name);
if (object_flags & R_OBJECT_NORMAL) {
2004-05-01 09:37:24 +00:00
// Normal scene object - display as subject of verb
snprintf(new_status, R_STATUS_TEXT_LEN, "%s %s", I_VerbData[_activeVerb].verb_str, object_name);
} else {
2004-05-01 09:37:24 +00:00
// Not normal scene object - override verb as we can only
// walk to this object
snprintf(new_status, R_STATUS_TEXT_LEN, "%s %s", I_VerbData[I_VERB_WALKTO].verb_str, object_name);
}
setStatusText(new_status);
return R_SUCCESS;
}
int Interface::hitTest(R_POINT *imouse_pt, int *ibutton) {
R_INTERFACE_BUTTON *buttons;
int nbuttons;
int xbase;
int ybase;
int i;
buttons = _cPanel.buttons;
nbuttons = _cPanel.nbuttons;
xbase = _cPanel.x;
ybase = _cPanel.y;
for (i = 0; i < nbuttons; i++) {
2004-05-01 09:37:24 +00:00
if ((imouse_pt->x >= (xbase + buttons[i].x1)) && (imouse_pt->x < (xbase + buttons[i].x2)) &&
(imouse_pt->y >= (ybase + buttons[i].y1)) && (imouse_pt->y < (ybase + buttons[i].y2))) {
*ibutton = i;
return R_SUCCESS;
}
}
*ibutton = -1;
return R_FAILURE;
}
} // End of namespace Saga