scummvm/gui.cpp
2002-02-24 17:25:03 +00:00

530 lines
10 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 The ScummVM project
*
* 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$
*/
#include "stdafx.h"
#include "scumm.h"
#include "gui.h"
enum {
SAVELOAD_DIALOG,
PAUSE_DIALOG
};
void Gui::draw(int start,int end) {
int i;
if (end==-1)
end=start;
for (i=0; i<sizeof(_widgets) / sizeof(_widgets[0]); i++) {
const GuiWidget *w = _widgets[i];
if (w) {
_parentX = 0;
_parentY = 0;
while (w->_type != GUI_NONE) {
if (w->_id>=start && w->_id<=end && (w->_page&(1<<_cur_page)) ) {
drawWidget(w);
}
if (w->_flags & GWF_PARENT) {
_parentX += w->_x;
_parentY += w->_y;
}
w++;
}
}
}
}
const GuiWidget *Gui::widgetFromPos(int x, int y) {
int i;
for (i=sizeof(_widgets) / sizeof(_widgets[0])-1; i>=0; i--) {
const GuiWidget *w = _widgets[i];
if (w) {
while (w->_type != GUI_NONE) {
if ((w->_page&(1<<_cur_page)) && w->_id &&
(uint16)(x-w->_x) < w->_w && (uint16)(y-w->_y) < w->_h)
return w;
if (w->_flags & GWF_PARENT) {
x -= w->_x;
y -= w->_y;
}
w++;
}
}
}
return NULL;
}
void Gui::drawString(const char *str, int x, int y, int w, byte color, bool center) {
StringTab *st = &_s->string[5];
st->charset = 1;
st->center = center;
st->color = color;
st->xpos = x;
st->ypos = y;
st->right = x + w;
_s->_messagePtr = (byte*)str;
_s->drawString(5);
}
void Gui::drawWidget(const GuiWidget *w) {
const char *s;
int x,y;
x = w->_x;
y = w->_y;
if (w->_flags & GWF_CLEARBG)
widgetClear(w);
if (w->_flags & GWF_BORDER) {
widgetBorder(w);
x += 4;
y += 4;
}
switch(w->_type) {
case GUI_TEXT:
s = queryString(w->_string_number,w->_id);
if (s)
drawString(s, x+_parentX, y+_parentY, w->_w,
(_clickWidget && _clickWidget==w->_id) ? _textcolorhi : _textcolor,
false);
break;
case GUI_IMAGE:
;
}
}
void Gui::widgetClear(const GuiWidget *wid) {
int x,y,w,h,i;
x = wid->_x;
y = wid->_y;
w = wid->_w;
h = wid->_h;
byte *ptr = getBasePtr(x,y);
if(ptr==NULL)
return;
_s->setVirtscreenDirty(_vs, x+_parentX, y+_parentY, x+_parentX+w, y+_parentY+h);
if (wid->_flags & GWF_BORDER) {
ptr += 320 + 1;
w-=2;
h-=2;
}
while (--h>=0) {
for(i=0; i<w; i++)
ptr[i] = _bgcolor;
ptr += 320;
}
}
void Gui::widgetBorder(const GuiWidget *w) {
int x = w->_x, y = w->_y;
int x2 = x + w->_w-1, y2 = y + w->_h-1;
byte tmp;
hline(x+1,y,x2-1);
hline(x,y+1,x2);
vline(x,y+1,y2-1);
vline(x+1,y,y2);
tmp = _color;
_color = _shadowcolor;
hline(x+1,y2-1,x2);
hline(x+1,y2,x2-1);
vline(x2,y+1,y2-1);
vline(x2-1,y+1,y2);
_color = tmp;
}
void Gui::hline(int x, int y, int x2) {
moveto(x,y);
lineto(x2,y);
}
void Gui::vline(int x, int y, int y2) {
moveto(x,y);
lineto(x,y2);
}
void Gui::moveto(int x, int y) {
_curX = x;
_curY = y;
}
byte *Gui::getBasePtr(int x, int y) {
x += _parentX;
y += _parentY;
_vs = _s->findVirtScreen(y);
if (_vs==NULL)
return NULL;
return _s->getResourceAddress(rtBuffer, _vs->number+1) + x + (y-_vs->topline)*320 + _s->_screenStartStrip*8;
}
void Gui::lineto(int x, int y) {
byte *ptr;
int x2 = _curX;
int y2 = _curY;
_curX = x;
_curY = y;
if (x2 < x)
x2^=x^=x2^=x;
if (y2 < y)
y2^=y^=y2^=y;
ptr = getBasePtr(x, y);
if (ptr==NULL)
return;
if (x==x2) {
/* vertical line */
while (y++ <= y2) {
*ptr = _color;
ptr += 320;
}
} else if (y==y2) {
/* horizontal line */
while (x++ <= x2) {
*ptr++ = _color;
}
}
}
void Gui::leftMouseClick(int x, int y) {
const GuiWidget *w = widgetFromPos(x,y);
int old;
_clickTimer = 0;
old = _clickWidget;
_clickWidget = w?w->_id : 0;
if (old)
draw(old);
if (_clickWidget) {
draw(_clickWidget);
if(w->_flags&GWF_DELAY)
_clickTimer = 5;
else
handleCommand(_clickWidget);
}
}
const GuiWidget save_load_dialog[] = {
{GUI_STAT,0xFF,GWF_DEFAULT|GWF_PARENT,30,20,260,120,0,0},
{GUI_TEXT,0x01,0,40,5,128,16,0,1}, /* How may I serve you? */
{GUI_TEXT,0x02,0,40,5,128,16,0,2}, /* Select a game to LOAD */
{GUI_TEXT,0x04,0,40,5,128,16,0,3}, /* Name your SAVE game */
{GUI_STAT,0xFF,GWF_DEFAULT,6,16,170,96,0,0},
{GUI_TEXT,0x01,GWF_DEFAULT,180,20,16,40,0,0},
{GUI_TEXT,0x01,GWF_DEFAULT,180,66,16,40,0,0},
{GUI_TEXT,0xFE,GWF_DEFAULT,180,20,16,40,1,0},
{GUI_TEXT,0xFE,GWF_DEFAULT,180,66,16,40,2,0},
{GUI_TEXT,0x06,GWF_CLEARBG,10,20,160,10,20,0},
{GUI_TEXT,0x06,GWF_CLEARBG,10,30,160,10,21,0},
{GUI_TEXT,0x06,GWF_CLEARBG,10,40,160,10,22,0},
{GUI_TEXT,0x06,GWF_CLEARBG,10,50,160,10,23,0},
{GUI_TEXT,0x06,GWF_CLEARBG,10,60,160,10,24,0},
{GUI_TEXT,0x06,GWF_CLEARBG,10,70,160,10,25,0},
{GUI_TEXT,0x06,GWF_CLEARBG,10,80,160,10,26,0},
{GUI_TEXT,0x06,GWF_CLEARBG,10,90,160,10,27,0},
{GUI_TEXT,0x06,GWF_CLEARBG,10,100,160,10,28,0},
{GUI_TEXT,0x01,GWF_BUTTON,200,25,54,16,3,4}, /* Save */
{GUI_TEXT,0x01,GWF_BUTTON,200,45,54,16,4,5}, /* Load */
{GUI_TEXT,0x01,GWF_BUTTON,200,65,54,16,5,6}, /* Play */
{GUI_TEXT,0x01,GWF_BUTTON,200,85,54,16,6,8}, /* Quit */
{GUI_TEXT,0x02,GWF_BUTTON,200,50,54,16,7,7}, /* Cancel */
{GUI_TEXT,0x04,GWF_BUTTON,200,45,54,16,8,9}, /* Ok */
{GUI_TEXT,0x04,GWF_BUTTON,200,65,54,16,7,7}, /* Cancel */
{0}
};
const GuiWidget pause_dialog[] = {
{GUI_TEXT,0x01,GWF_DEFAULT,50,80,220,16,0,10},
{0},
};
void Gui::handleCommand(int cmd) {
int lastEdit = _editString;
showCaret(false);
switch(cmd) {
case 1: /* up button */
if (_slotIndex==0)
return;
getSavegameNames(_slotIndex-9);
draw(20,28);
return;
case 2:
if (_slotIndex > 80)
return;
getSavegameNames(_slotIndex+9);
draw(20,28);
return;
case 3: /* save button */
_cur_page = 2;
getSavegameNames(0);
draw(0,100);
return;
case 4: /* load button */
_cur_page = 1;
getSavegameNames(0);
draw(0,100);
return;
case 5: /* play button */
close();
return;
case 6: /* quit button */
exit(1);
return;
case 7: /* cancel button */
_cur_page = 0;
draw(0,100);
return;
case 8:
if (lastEdit==-1 || game_names[lastEdit][0]==0)
return;
_s->_saveLoadSlot = lastEdit + _slotIndex;
_s->_saveLoadCompatible = false;
_s->_saveLoadFlag = 1;
memcpy(_s->_saveLoadName, game_names[lastEdit], sizeof(_s->_saveLoadName));
close();
return;
default:
if (cmd>=20 && cmd<=28) {
if(_cur_page==1) {
if (valid_games[cmd-20]) {
_s->_saveLoadSlot = cmd-20+_slotIndex;
_s->_saveLoadCompatible = false;
_s->_saveLoadFlag = 2;
close();
}
return;
} else if (_cur_page==2) {
_clickWidget = cmd;
editString(cmd - 20);
}
}
}
}
void Gui::getSavegameNames(int start) {
int i;
_slotIndex = start;
for(i=0; i<9; i++,start++) {
valid_games[i] = _s->getSavegameName(start, game_names[i]);
}
}
const byte string_map_table_v6[] = {
117, /* How may I serve you? */
109, /* Select a game to LOAD */
108, /* Name your SAVE game */
96, /* Save */
97, /* Load */
98, /* Play */
99, /* Cancel */
100, /* Quit */
101, /* Ok */
93, /* Game paused */
};
const byte string_map_table_v5[] = {
0, /* How may I serve you? */
20, /* Select a game to LOAD */
19, /* Name your SAVE game */
7, /* Save */
8, /* Load */
9, /* Play */
10, /* Cancel */
11, /* Quit */
12, /* Ok */
4, /* Game paused */
};
const char *Gui::queryString(int string, int id) {
static char namebuf[64];
if (id>=20 && id<=28) {
sprintf(namebuf, "%2d. %s", id-20+_slotIndex, game_names[id-20]);
return namebuf;
}
if (string == 0)
return NULL;
if (_s->_features&GF_AFTER_V6) {
string = _s->_vars[string_map_table_v6[string-1]];
} else {
string = string_map_table_v5[string-1];
}
return (char*)_s->getStringAddress(string);
}
void Gui::showCaret(bool show) {
int i;
char *s;
if (_editString==-1)
return;
i = _editLen;
s = game_names[_editString];
if (show) {
if (i < SAVEGAME_NAME_LEN-1) {
s[i] = '_';
s[i+1] = 0;
}
} else {
s[i] = 0;
}
draw(_editString + 20);
if (!show)
_editString = -1;
}
void Gui::editString(int i) {
char *s = game_names[i];
if(!valid_games[i]) {
valid_games[i] = true;
*s = 0;
}
_editString = i;
_editLen = strlen(s);
showCaret(true);
}
void Gui::addLetter(byte letter) {
switch(_dialog) {
case SAVELOAD_DIALOG:
if (_editString==-1)
return;
if (letter==13) {
handleCommand(8);
return;
}
if (letter>=32 && letter<128 && _editLen < SAVEGAME_NAME_LEN-1) {
game_names[_editString][_editLen++] = letter;
} else if (letter==8 && _editLen>0) {
_editLen--;
}
showCaret(true);
break;
case PAUSE_DIALOG:
if (letter==32)
close();
break;
}
}
byte Gui::getDefaultColor(int color) {
#if defined(FULL_THROTTLE)
return 0;
#else
if (_s->_features&GF_AFTER_V6) {
if (color==8) color=1;
return _s->readArray(110, 0, color);
} else {
return _s->getStringAddress(21)[color];
}
#endif
}
void Gui::init(Scumm *s) {
_s = s;
_bgcolor = getDefaultColor(0);
_color = getDefaultColor(1);
_textcolor = getDefaultColor(2);
_textcolorhi = getDefaultColor(6);
_shadowcolor = getDefaultColor(8);
}
void Gui::loop() {
if (_active==1) {
_active++;
draw(0,100);
_s->_cursorAnimate++;
_s->gdi._cursorActive = 1;
_s->pauseSounds(true);
}
_s->getKeyInput(0);
if (_s->_mouseButStat&MBS_LEFT_CLICK) {
leftMouseClick(_s->mouse.x, _s->mouse.y);
} else if (_s->_lastKeyHit) {
addLetter((unsigned char)_s->_lastKeyHit);
}
if (_clickTimer && !--_clickTimer) {
int old = _clickWidget;
_clickWidget = 0;
draw(old);
handleCommand(old);
}
_s->drawDirtyScreenParts();
_s->_mouseButStat = 0;
}
void Gui::close() {
_s->_fullRedraw = true;
_s->_completeScreenRedraw = true;
_s->_cursorAnimate--;
_s->pauseSounds(false);
_active = false;
}
void Gui::saveLoadDialog() {
_widgets[0] = save_load_dialog;
_editString = -1;
_cur_page = 0;
_active = true;
_dialog = SAVELOAD_DIALOG;
}
void Gui::pause() {
_widgets[0] = pause_dialog;
_active = true;
_dialog = PAUSE_DIALOG;
}